From 11553b0de8992ded6240d034bd49f561d17bea53 Mon Sep 17 00:00:00 2001
From: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
Date: Thu, 13 Jun 2013 01:18:02 +0200
Subject: MIPS: add support for Lantiq XWAY SoCs

Signed-off-by: Luka Perkov <luka@openwrt.org>
Signed-off-by: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>

--- a/.gitignore
+++ b/.gitignore
@@ -49,6 +49,13 @@
 /u-boot.sb
 /u-boot.bd
 /u-boot.geany
+/u-boot.bin.lzma
+/u-boot.bin.lzo
+/u-boot.ltq.lzma.norspl
+/u-boot.ltq.lzo.norspl
+/u-boot.ltq.norspl
+/u-boot.lzma.img
+/u-boot.lzo.img
 
 #
 # Generated files
--- a/Makefile
+++ b/Makefile
@@ -435,6 +435,12 @@ $(obj)u-boot.bin:	$(obj)u-boot
 		$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
 		$(BOARD_SIZE_CHECK)
 
+$(obj)u-boot.bin.lzma:	$(obj)u-boot.bin
+		cat $< | lzma -9 -f - > $@
+
+$(obj)u-boot.bin.lzo:	$(obj)u-boot.bin
+		cat $< | lzop -9 -f - > $@
+
 $(obj)u-boot.ldr:	$(obj)u-boot
 		$(CREATE_LDR_ENV)
 		$(LDR) -T $(CONFIG_BFIN_CPU) -c $@ $< $(LDR_FLAGS)
@@ -454,13 +460,23 @@ ifndef CONFIG_SYS_UBOOT_START
 CONFIG_SYS_UBOOT_START := 0
 endif
 
-$(obj)u-boot.img:	$(obj)u-boot.bin
-		$(obj)tools/mkimage -A $(ARCH) -T firmware -C none \
+define GEN_UBOOT_IMAGE
+		$(obj)tools/mkimage -A $(ARCH) -T firmware -C $(1) \
 		-O u-boot -a $(CONFIG_SYS_TEXT_BASE) \
 		-e $(CONFIG_SYS_UBOOT_START) \
 		-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
 			sed -e 's/"[	 ]*$$/ for $(BOARD) board"/') \
 		-d $< $@
+endef
+
+$(obj)u-boot.img:	$(obj)u-boot.bin
+		$(call GEN_UBOOT_IMAGE,none)
+
+$(obj)u-boot.lzma.img:	$(obj)u-boot.bin.lzma
+		$(call GEN_UBOOT_IMAGE,lzma)
+
+$(obj)u-boot.lzo.img:	$(obj)u-boot.bin.lzo
+		$(call GEN_UBOOT_IMAGE,lzo)
 
 $(obj)u-boot.imx: $(obj)u-boot.bin depend
 		$(MAKE) -C $(SRCTREE)/arch/arm/imx-common $(OBJTREE)/u-boot.imx
@@ -571,6 +587,27 @@ $(obj)u-boot-img-spl-at-end.bin: $(obj)s
 			conv=notrunc 2>/dev/null
 		cat $(obj)u-boot-pad.img $(obj)spl/u-boot-spl.bin > $@
 
+$(obj)u-boot.ltq.sfspl:	$(obj)u-boot.img $(obj)spl/u-boot-spl.bin
+		$(obj)tools/ltq-boot-image -t sfspl -e $(CONFIG_SPL_TEXT_BASE) \
+			-s $(obj)spl/u-boot-spl.bin -u $< -o $@
+
+$(obj)u-boot.ltq.lzo.sfspl: $(obj)u-boot.lzo.img $(obj)spl/u-boot-spl.bin
+		$(obj)tools/ltq-boot-image -t sfspl -e $(CONFIG_SPL_TEXT_BASE) \
+			-s $(obj)spl/u-boot-spl.bin -u $< -o $@
+
+$(obj)u-boot.ltq.lzma.sfspl: $(obj)u-boot.lzma.img $(obj)spl/u-boot-spl.bin
+		$(obj)tools/ltq-boot-image -t sfspl -e $(CONFIG_SPL_TEXT_BASE) \
+			-s $(obj)spl/u-boot-spl.bin -u $< -o $@
+
+$(obj)u-boot.ltq.norspl: $(obj)u-boot.img $(obj)spl/u-boot-spl.bin
+	cat $(obj)spl/u-boot-spl.bin $< > $@
+
+$(obj)u-boot.ltq.lzo.norspl: $(obj)u-boot.lzo.img $(obj)spl/u-boot-spl.bin
+	cat $(obj)spl/u-boot-spl.bin $< > $@
+
+$(obj)u-boot.ltq.lzma.norspl: $(obj)u-boot.lzma.img $(obj)spl/u-boot-spl.bin
+	cat $(obj)spl/u-boot-spl.bin $< > $@
+
 ifeq ($(CONFIG_SANDBOX),y)
 GEN_UBOOT = \
 		cd $(LNDIR) && $(CC) $(SYMS) -T $(obj)u-boot.lds \
--- a/README
+++ b/README
@@ -468,6 +468,11 @@ The following options need to be configu
 			CONF_CM_CACHABLE_CUW
 			CONF_CM_CACHABLE_ACCELERATED
 
+		CONFIG_SYS_MIPS_CACHE_EXT_INIT
+
+		Enable this to use extended cache initialization for recent
+		MIPS CPU cores.
+
 		CONFIG_SYS_XWAY_EBU_BOOTCFG
 
 		Special option for Lantiq XWAY SoCs for booting from NOR flash.
--- a/arch/mips/config.mk
+++ b/arch/mips/config.mk
@@ -45,9 +45,13 @@ PLATFORM_CPPFLAGS += -DCONFIG_MIPS -D__M
 # On the other hand, we want PIC in the U-Boot code to relocate it from ROM
 # to RAM. $28 is always used as gp.
 #
-PLATFORM_CPPFLAGS		+= -G 0 -mabicalls -fpic $(ENDIANNESS)
+PF_ABICALLS			?= -mabicalls
+PF_PIC				?= -fpic
+PF_PIE				?= -pie
+
+PLATFORM_CPPFLAGS		+= -G 0 $(PF_ABICALLS) $(PF_PIC) $(ENDIANNESS)
 PLATFORM_CPPFLAGS		+= -msoft-float
 PLATFORM_LDFLAGS		+= -G 0 -static -n -nostdlib $(ENDIANNESS)
 PLATFORM_RELFLAGS		+= -ffunction-sections -fdata-sections
-LDFLAGS_FINAL			+= --gc-sections -pie
+LDFLAGS_FINAL			+= --gc-sections $(PF_PIE)
 OBJCFLAGS			+= --remove-section=.dynsym
--- a/arch/mips/cpu/mips32/cache.S
+++ b/arch/mips/cpu/mips32/cache.S
@@ -29,7 +29,11 @@
  */
 #define MIPS_MAX_CACHE_SIZE	0x10000
 
+#ifdef CONFIG_SYS_MIPS_CACHE_EXT_INIT
+#define INDEX_BASE	0x9fc00000
+#else
 #define INDEX_BASE	CKSEG0
+#endif
 
 	.macro	cache_op op addr
 	.set	push
@@ -65,7 +69,11 @@
  */
 LEAF(mips_init_icache)
 	blez		a1, 9f
+#ifdef CONFIG_SYS_MIPS_CACHE_EXT_INIT
+	mtc0		zero, CP0_ITAGLO
+#else
 	mtc0		zero, CP0_TAGLO
+#endif
 	/* clear tag to invalidate */
 	PTR_LI		t0, INDEX_BASE
 	PTR_ADDU	t1, t0, a1
@@ -90,7 +98,11 @@ LEAF(mips_init_icache)
  */
 LEAF(mips_init_dcache)
 	blez		a1, 9f
+#ifdef CONFIG_SYS_MIPS_CACHE_EXT_INIT
+	mtc0		zero, CP0_DTAGLO
+#else
 	mtc0		zero, CP0_TAGLO
+#endif
 	/* clear all tags */
 	PTR_LI		t0, INDEX_BASE
 	PTR_ADDU	t1, t0, a1
--- /dev/null
+++ b/arch/mips/cpu/mips32/danube/Makefile
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2000-2011 Wolfgang Denk, DENX Software Engineering, wd@denx.de
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+include $(TOPDIR)/config.mk
+
+LIB	= $(obj)lib$(SOC).o
+
+COBJS-y	+= cgu.o chipid.o ebu.o mem.o pmu.o rcu.o
+SOBJS-y	+= cgu_init.o mem_init.o
+
+COBJS	:= $(COBJS-y)
+SOBJS	:= $(SOBJS-y)
+SRCS	:= $(SOBJS:.o=.S) $(COBJS:.o=.c)
+OBJS	:= $(addprefix $(obj),$(SOBJS) $(COBJS))
+
+all:	$(LIB)
+
+$(LIB):	$(obj).depend $(OBJS)
+	$(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
--- /dev/null
+++ b/arch/mips/cpu/mips32/danube/cgu.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2007-2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/soc.h>
+#include <asm/lantiq/clk.h>
+#include <asm/lantiq/io.h>
+
+#define LTQ_CGU_SYS_DDR_MASK		0x0003
+#define LTQ_CGU_SYS_DDR_SHIFT		0
+#define LTQ_CGU_SYS_CPU0_MASK		0x000C
+#define LTQ_CGU_SYS_CPU0_SHIFT		2
+#define LTQ_CGU_SYS_FPI_MASK		0x0040
+#define LTQ_CGU_SYS_FPI_SHIFT		6
+
+struct ltq_cgu_regs {
+	u32	rsvd0;
+	u32	pll0_cfg;	/* PLL0 config */
+	u32	pll1_cfg;	/* PLL1 config */
+	u32	pll2_cfg;	/* PLL2 config */
+	u32	sys;		/* System clock */
+	u32	update;		/* CGU update control */
+	u32	if_clk;		/* Interface clock */
+	u32	osc_con;	/* Update OSC Control */
+	u32	smd;		/* SDRAM Memory Control */
+	u32	rsvd1[3];
+	u32	pcm_cr;		/* PCM control */
+	u32	pci_cr;		/* PCI clock control */
+};
+
+static struct ltq_cgu_regs *ltq_cgu_regs =
+	(struct ltq_cgu_regs *) CKSEG1ADDR(LTQ_CGU_BASE);
+
+static inline u32 ltq_cgu_sys_readl(u32 mask, u32 shift)
+{
+	return (ltq_readl(&ltq_cgu_regs->sys) & mask) >> shift;
+}
+
+unsigned long ltq_get_io_region_clock(void)
+{
+	u32 ddr_sel;
+	unsigned long clk;
+
+	ddr_sel = ltq_cgu_sys_readl(LTQ_CGU_SYS_DDR_MASK,
+					LTQ_CGU_SYS_DDR_SHIFT);
+
+	switch (ddr_sel) {
+	case 0:
+		clk = CLOCK_166_MHZ;
+		break;
+	case 1:
+		clk = CLOCK_133_MHZ;
+		break;
+	case 2:
+		clk = CLOCK_111_MHZ;
+		break;
+	case 3:
+		clk = CLOCK_83_MHZ;
+		break;
+	default:
+		clk = 0;
+		break;
+	}
+
+	return clk;
+}
+
+unsigned long ltq_get_cpu_clock(void)
+{
+	u32 cpu0_sel;
+	unsigned long clk;
+
+	cpu0_sel = ltq_cgu_sys_readl(LTQ_CGU_SYS_CPU0_MASK,
+					LTQ_CGU_SYS_CPU0_SHIFT);
+
+	switch (cpu0_sel) {
+		/* Same as PLL0 output (333,33 MHz) */
+	case 0:
+		clk = CLOCK_333_MHZ;
+		break;
+		/* 1/1 fixed ratio to DDR clock */
+	case 1:
+		clk = ltq_get_io_region_clock();
+		break;
+		/* 1/2 fixed ratio to DDR clock */
+	case 2:
+		clk = ltq_get_io_region_clock() << 1;
+		break;
+	default:
+		clk = 0;
+		break;
+	}
+
+	return clk;
+}
+
+unsigned long ltq_get_bus_clock(void)
+{
+	u32 fpi_sel;
+	unsigned long clk;
+
+	fpi_sel = ltq_cgu_sys_readl(LTQ_CGU_SYS_FPI_MASK,
+					LTQ_CGU_SYS_FPI_SHIFT);
+
+	if (fpi_sel)
+		/* Half the DDR clock */
+		clk = ltq_get_io_region_clock() >> 1;
+	else
+		/* Same as DDR clock */
+		clk = ltq_get_io_region_clock();
+
+	return clk;
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/danube/cgu_init.S
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2007-2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <config.h>
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/addrspace.h>
+#include <asm/arch/soc.h>
+
+/* RCU module register */
+#define LTQ_RCU_RST_REQ			0x0010
+#define LTQ_RCU_RST_STAT		0x0014
+#define LTQ_RCU_RST_REQ_VALUE		0x40000008
+#define LTQ_RCU_RST_STAT_XTAL_F	0x20000
+
+/* CGU module register */
+#define LTQ_CGU_PLL0_CFG		0x0004	/* PLL0 config */
+#define LTQ_CGU_PLL1_CFG		0x0008	/* PLL1 config */
+#define LTQ_CGU_PLL2_CFG		0x000C	/* PLL2 config */
+#define LTQ_CGU_SYS			0x0010	/* System clock */
+
+/* Valid SYS.CPU0/1 values */
+#define LTQ_CGU_SYS_CPU0_SHIFT		2
+#define LTQ_CGU_SYS_CPU1_SHIFT		4
+#define LTQ_CGU_SYS_CPU_PLL0		0x0
+#define LTQ_CGU_SYS_CPU_DDR_EQUAL	0x1
+#define LTQ_CGU_SYS_CPU_DDR_TWICE	0x2
+
+/* Valid SYS.DDR values */
+#define LTQ_CGU_SYS_DDR_SHIFT		0
+#define LTQ_CGU_SYS_DDR_167_MHZ	0x0
+#define LTQ_CGU_SYS_DDR_133_MHZ	0x1
+#define LTQ_CGU_SYS_DDR_111_MHZ	0x2
+#define LTQ_CGU_SYS_DDR_83_MHZ		0x3
+
+/* Valid SYS.FPI values */
+#define LTQ_CGU_SYS_FPI_SHIFT		6
+#define LTQ_CGU_SYS_FPI_DDR_EQUAL	0x0
+#define LTQ_CGU_SYS_FPI_DDR_HALF	0x1
+
+/* Valid SYS.PPE values */
+#define LTQ_CGU_SYS_PPE_SHIFT		7
+#define LTQ_CGU_SYS_PPE_266_MHZ	0x0
+#define LTQ_CGU_SYS_PPE_240_MHZ	0x1
+#define LTQ_CGU_SYS_PPE_222_MHZ	0x2
+#define LTQ_CGU_SYS_PPE_133_MHZ	0x3
+
+#if (CONFIG_SYS_CLOCK_MODE == LTQ_CLK_CPU_333_DDR_167)
+#define LTQ_CGU_SYS_CPU_CONFIG		LTQ_CGU_SYS_CPU_DDR_TWICE
+#define LTQ_CGU_SYS_DDR_CONFIG		LTQ_CGU_SYS_DDR_167_MHZ
+#define LTQ_CGU_SYS_FPI_CONFIG		LTQ_CGU_SYS_FPI_DDR_HALF
+#define LTQ_CGU_SYS_PPE_CONFIG		LTQ_CGU_SYS_PPE_266_MHZ
+#elif (CONFIG_SYS_CLOCK_MODE == LTQ_CLK_CPU_111_DDR_111)
+#define LTQ_CGU_SYS_CPU_CONFIG		LTQ_CGU_SYS_CPU_DDR_EQUAL
+#define LTQ_CGU_SYS_DDR_CONFIG		LTQ_CGU_SYS_DDR_111_MHZ
+#define LTQ_CGU_SYS_FPI_CONFIG		LTQ_CGU_SYS_FPI_DDR_HALF
+#define LTQ_CGU_SYS_PPE_CONFIG		LTQ_CGU_SYS_PPE_133_MHZ
+#else
+#error "Invalid system clock configuration!"
+#endif
+
+/* Build register values */
+#define LTQ_CGU_SYS_VALUE	((LTQ_CGU_SYS_PPE_CONFIG << \
+					LTQ_CGU_SYS_PPE_SHIFT) | \
+				(LTQ_CGU_SYS_FPI_CONFIG << \
+					LTQ_CGU_SYS_FPI_SHIFT) | \
+				(LTQ_CGU_SYS_CPU_CONFIG << \
+					LTQ_CGU_SYS_CPU1_SHIFT) | \
+				(LTQ_CGU_SYS_CPU_CONFIG << \
+					LTQ_CGU_SYS_CPU0_SHIFT) | \
+				LTQ_CGU_SYS_DDR_CONFIG)
+
+/* Reset values for PLL registers for usage with 35.328 MHz crystal */
+#define PLL0_35MHZ_CONFIG	0x9D861059
+#define PLL1_35MHZ_CONFIG	0x1A260CD9
+#define PLL2_35MHZ_CONFIG	0x8000f1e5
+
+/* Reset values for PLL registers for usage with 36 MHz crystal */
+#define PLL0_36MHZ_CONFIG	0x1000125D
+#define PLL1_36MHZ_CONFIG	0x1B1E0C99
+#define PLL2_36MHZ_CONFIG	0x8002f2a1
+
+LEAF(ltq_cgu_init)
+	/* Load current CGU register value */
+	li	t0, (LTQ_CGU_BASE | KSEG1)
+	lw	t1, LTQ_CGU_SYS(t0)
+
+	/* Load target CGU register values */
+	li	t3, LTQ_CGU_SYS_VALUE
+
+	/* Only update registers if values differ */
+	beq	t1, t3, finished
+
+	/*
+	 * Check whether the XTAL_F bit in RST_STAT register is set or not.
+	 * This bit is latched in via pin strapping. If bit is set then
+	 * clock source is a 36 MHz crystal. Otherwise a 35.328 MHz crystal.
+	 */
+	 li	t1, (LTQ_RCU_BASE | KSEG1)
+	 lw	t2, LTQ_RCU_RST_STAT(t1)
+	 and	t2, t2, LTQ_RCU_RST_STAT_XTAL_F
+	 beq	t2, LTQ_RCU_RST_STAT_XTAL_F, boot_36mhz
+
+boot_35mhz:
+	/* Configure PLL for 35.328 MHz */
+	li	t2, PLL0_35MHZ_CONFIG
+	sw	t2, LTQ_CGU_PLL0_CFG(t0)
+	li	t2, PLL1_35MHZ_CONFIG
+	sw	t2, LTQ_CGU_PLL1_CFG(t0)
+	li	t2, PLL2_35MHZ_CONFIG
+	sw	t2, LTQ_CGU_PLL2_CFG(t0)
+
+	b	do_reset
+
+boot_36mhz:
+	/* Configure PLL for 36 MHz */
+	li	t2, PLL0_36MHZ_CONFIG
+	sw	t2, LTQ_CGU_PLL0_CFG(t0)
+	li	t2, PLL1_36MHZ_CONFIG
+	sw	t2, LTQ_CGU_PLL1_CFG(t0)
+	li	t2, PLL2_36MHZ_CONFIG
+	sw	t2, LTQ_CGU_PLL2_CFG(t0)
+
+do_reset:
+	/* Store new clock config */
+	sw	t3, LTQ_CGU_SYS(t0)
+
+	/* Perform software reset to activate new clock config */
+	li	t2, LTQ_RCU_RST_REQ_VALUE
+	sw	t2, LTQ_RCU_RST_REQ(t1)
+
+wait_reset:
+	b	wait_reset
+
+finished:
+	jr	ra
+
+	END(ltq_cgu_init)
--- /dev/null
+++ b/arch/mips/cpu/mips32/danube/chipid.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/lantiq/io.h>
+#include <asm/lantiq/chipid.h>
+#include <asm/arch/soc.h>
+
+#define LTQ_CHIPID_VERSION_SHIFT	28
+#define LTQ_CHIPID_VERSION_MASK		(0xF << LTQ_CHIPID_VERSION_SHIFT)
+#define LTQ_CHIPID_PNUM_SHIFT		12
+#define LTQ_CHIPID_PNUM_MASK		(0xFFFF << LTQ_CHIPID_PNUM_SHIFT)
+
+struct ltq_chipid_regs {
+	u32	manid;		/* Manufacturer identification */
+	u32	chipid;		/* Chip identification */
+};
+
+static struct ltq_chipid_regs *ltq_chipid_regs =
+	(struct ltq_chipid_regs *) CKSEG1ADDR(LTQ_CHIPID_BASE);
+
+unsigned int ltq_chip_version_get(void)
+{
+	u32 chipid;
+
+	chipid = ltq_readl(&ltq_chipid_regs->chipid);
+
+	return (chipid & LTQ_CHIPID_VERSION_MASK) >> LTQ_CHIPID_VERSION_SHIFT;
+}
+
+unsigned int ltq_chip_partnum_get(void)
+{
+	u32 chipid;
+
+	chipid = ltq_readl(&ltq_chipid_regs->chipid);
+
+	return (chipid & LTQ_CHIPID_PNUM_MASK) >> LTQ_CHIPID_PNUM_SHIFT;
+}
+
+const char *ltq_chip_partnum_str(void)
+{
+	enum ltq_chip_partnum partnum = ltq_chip_partnum_get();
+
+	switch (partnum) {
+	case LTQ_SOC_DANUBE:
+		return "Danube";
+	case LTQ_SOC_DANUBE_S:
+		return "Danube-S";
+	case LTQ_SOC_TWINPASS:
+		return "Twinpass";
+	default:
+		printf("Unknown partnum: %x\n", partnum);
+	}
+
+	return "";
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/danube/config.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+PF_CPPFLAGS_DANUBE := $(call cc-option,-mtune=24kec,)
+PLATFORM_CPPFLAGS += $(PF_CPPFLAGS_DANUBE)
+
+ifdef CONFIG_SPL_BUILD
+PF_ABICALLS		:= -mno-abicalls
+PF_PIC			:= -fno-pic
+PF_PIE			:=
+USE_PRIVATE_LIBGCC	:= yes
+endif
+
+LIBS-y += $(CPUDIR)/lantiq-common/liblantiq-common.o
+
+ifndef CONFIG_SPL_BUILD
+ifdef CONFIG_SYS_BOOT_NORSPL
+ALL-y += $(obj)u-boot.ltq.norspl
+ALL-$(CONFIG_SPL_LZO_SUPPORT) += $(obj)u-boot.ltq.lzo.norspl
+ALL-$(CONFIG_SPL_LZMA_SUPPORT) += $(obj)u-boot.ltq.lzma.norspl
+endif
+endif
--- /dev/null
+++ b/arch/mips/cpu/mips32/danube/ebu.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/soc.h>
+#include <asm/lantiq/io.h>
+
+#define EBU_ADDRSEL_MASK(mask)		((mask & 0xf) << 4)
+#define EBU_ADDRSEL_REGEN		(1 << 0)
+
+#define EBU_CON_WRDIS			(1 << 31)
+#define EBU_CON_AGEN_DEMUX		(0x0 << 24)
+#define EBU_CON_AGEN_MUX		(0x2 << 24)
+#define EBU_CON_SETUP			(1 << 22)
+#define EBU_CON_WAIT_DIS		(0x0 << 20)
+#define EBU_CON_WAIT_ASYNC		(0x1 << 20)
+#define EBU_CON_WAIT_SYNC		(0x2 << 20)
+#define EBU_CON_WINV			(1 << 19)
+#define EBU_CON_PW_8BIT			(0x0 << 16)
+#define EBU_CON_PW_16BIT		(0x1 << 16)
+#define EBU_CON_ALEC(cycles)		((cycles & 0x3) << 14)
+#define EBU_CON_BCGEN_CS		(0x0 << 12)
+#define EBU_CON_BCGEN_INTEL		(0x1 << 12)
+#define EBU_CON_BCGEN_MOTOROLA		(0x2 << 12)
+#define EBU_CON_WAITWRC(cycles)		((cycles & 0x7) << 8)
+#define EBU_CON_WAITRDC(cycles)		((cycles & 0x3) << 6)
+#define EBU_CON_HOLDC(cycles)		((cycles & 0x3) << 4)
+#define EBU_CON_RECOVC(cycles)		((cycles & 0x3) << 2)
+#define EBU_CON_CMULT_1			0x0
+#define EBU_CON_CMULT_4			0x1
+#define EBU_CON_CMULT_8			0x2
+#define EBU_CON_CMULT_16		0x3
+
+#if defined(CONFIG_LTQ_SUPPORT_NOR_FLASH)
+#define ebu_region0_enable		1
+#else
+#define ebu_region0_enable		0
+#endif
+
+#if defined(CONFIG_LTQ_SUPPORT_NAND_FLASH)
+#define ebu_region1_enable		1
+#else
+#define ebu_region1_enable		0
+#endif
+
+struct ltq_ebu_regs {
+	u32	clc;
+	u32	rsvd0[3];
+	u32	con;
+	u32	rsvd1[3];
+	u32	addr_sel_0;
+	u32	addr_sel_1;
+	u32	rsvd2[14];
+	u32	con_0;
+	u32	con_1;
+};
+
+static struct ltq_ebu_regs *ltq_ebu_regs =
+	(struct ltq_ebu_regs *) CKSEG1ADDR(LTQ_EBU_BASE);
+
+void ltq_ebu_init(void)
+{
+	if (ebu_region0_enable) {
+		/*
+		 * Map EBU region 0 to range 0x10000000-0x13ffffff and enable
+		 * region control. This supports up to 32 MiB NOR flash in
+		 * bank 0.
+		 */
+		ltq_writel(&ltq_ebu_regs->addr_sel_0, LTQ_EBU_REGION0_BASE |
+			EBU_ADDRSEL_MASK(1) | EBU_ADDRSEL_REGEN);
+
+		ltq_writel(&ltq_ebu_regs->con_0, EBU_CON_AGEN_DEMUX |
+			EBU_CON_WAIT_DIS | EBU_CON_PW_16BIT |
+			EBU_CON_ALEC(3) | EBU_CON_BCGEN_INTEL |
+			EBU_CON_WAITWRC(7) | EBU_CON_WAITRDC(3) |
+			EBU_CON_HOLDC(3) | EBU_CON_RECOVC(3) |
+			EBU_CON_CMULT_16);
+	} else
+		ltq_clrbits(&ltq_ebu_regs->addr_sel_0, EBU_ADDRSEL_REGEN);
+
+	if (ebu_region1_enable) {
+		/*
+		 * Map EBU region 1 to range 0x14000000-0x13ffffff and enable
+		 * region control. This supports NAND flash in bank 1.
+		 */
+		ltq_writel(&ltq_ebu_regs->addr_sel_1, LTQ_EBU_REGION1_BASE |
+			EBU_ADDRSEL_MASK(3) | EBU_ADDRSEL_REGEN);
+
+		ltq_writel(&ltq_ebu_regs->con_1, EBU_CON_AGEN_DEMUX |
+			EBU_CON_SETUP | EBU_CON_WAIT_DIS | EBU_CON_PW_8BIT |
+			EBU_CON_ALEC(3) | EBU_CON_BCGEN_INTEL |
+			EBU_CON_WAITWRC(2) | EBU_CON_WAITRDC(2) |
+			EBU_CON_HOLDC(1) | EBU_CON_RECOVC(1) |
+			EBU_CON_CMULT_4);
+	} else
+		ltq_clrbits(&ltq_ebu_regs->addr_sel_1, EBU_ADDRSEL_REGEN);
+}
+
+void *flash_swap_addr(unsigned long addr)
+{
+	return (void *)(addr ^ 2);
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/danube/mem.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/soc.h>
+#include <asm/lantiq/io.h>
+
+static void *ltq_mc_ddr_base = (void *) CKSEG1ADDR(LTQ_MC_DDR_BASE);
+
+static inline u32 ltq_mc_dc_read(u32 index)
+{
+	return ltq_readl(ltq_mc_ddr_base + LTQ_MC_DDR_DC_OFFSET(index));
+}
+
+phys_size_t initdram(int board_type)
+{
+	u32 col, row, dc04, dc19, dc20;
+
+	dc04 = ltq_mc_dc_read(4);
+	dc19 = ltq_mc_dc_read(19);
+	dc20 = ltq_mc_dc_read(20);
+
+	row = (dc04 & 0xF) - ((dc19 & 0x700) >> 8);
+	col = ((dc04 & 0xF00) >> 8) - (dc20 & 0x7);
+
+	return (1 << (row + col)) * 4 * 2;
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/danube/mem_init.S
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2007-2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <config.h>
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/addrspace.h>
+#include <asm/arch/soc.h>
+
+/* Must be configured in BOARDDIR */
+#include <ddr_settings.h>
+
+#define LTQ_MC_GEN_ERRCAUSE		0x0010
+#define LTQ_MC_GEN_ERRADDR		0x0020
+#define LTQ_MC_GEN_CON			0x0060
+#define LTQ_MC_GEN_STAT			0x0070
+#define LTQ_MC_GEN_CON_SRAM_DDR_ENABLE	0x5
+#define LTQ_MC_GEN_STAT_DLCK_PWRON	0xC
+
+#define LTQ_MC_DDR_DC03_MC_START	0x100
+
+	/* Store given value in MC DDR CCRx register */
+	.macro dc_sw num, val
+	li	t2, \val
+	sw	t2, LTQ_MC_DDR_DC_OFFSET(\num)(t1)
+	.endm
+
+LEAF(ltq_mem_init)
+	/* Load MC General and MC DDR module base */
+	li	t0, (LTQ_MC_GEN_BASE | KSEG1)
+	li	t1, (LTQ_MC_DDR_BASE | KSEG1)
+
+	/* Clear access error log registers */
+	sw	zero, LTQ_MC_GEN_ERRCAUSE(t0)
+	sw	zero, LTQ_MC_GEN_ERRADDR(t0)
+
+	/* Enable DDR and SRAM module in memory controller */
+	li	t2, LTQ_MC_GEN_CON_SRAM_DDR_ENABLE
+	sw	t2, LTQ_MC_GEN_CON(t0)
+
+	/* Clear start bit of DDR memory controller */
+	sw	zero, LTQ_MC_DDR_DC_OFFSET(3)(t1)
+
+	/* Init memory controller registers with values ddr_settings.h */
+	dc_sw	0, MC_DC00_VALUE
+	dc_sw	1, MC_DC01_VALUE
+	dc_sw	2, MC_DC02_VALUE
+	dc_sw	4, MC_DC04_VALUE
+	dc_sw	5, MC_DC05_VALUE
+	dc_sw	6, MC_DC06_VALUE
+	dc_sw	7, MC_DC07_VALUE
+	dc_sw	8, MC_DC08_VALUE
+	dc_sw	9, MC_DC09_VALUE
+
+	dc_sw	10, MC_DC10_VALUE
+	dc_sw	11, MC_DC11_VALUE
+	dc_sw	12, MC_DC12_VALUE
+	dc_sw	13, MC_DC13_VALUE
+	dc_sw	14, MC_DC14_VALUE
+	dc_sw	15, MC_DC15_VALUE
+	dc_sw	16, MC_DC16_VALUE
+	dc_sw	17, MC_DC17_VALUE
+	dc_sw	18, MC_DC18_VALUE
+	dc_sw	19, MC_DC19_VALUE
+
+	dc_sw	20, MC_DC20_VALUE
+	dc_sw	21, MC_DC21_VALUE
+	dc_sw	22, MC_DC22_VALUE
+	dc_sw	23, MC_DC23_VALUE
+	dc_sw	24, MC_DC24_VALUE
+	dc_sw	25, MC_DC25_VALUE
+	dc_sw	26, MC_DC26_VALUE
+	dc_sw	27, MC_DC27_VALUE
+	dc_sw	28, MC_DC28_VALUE
+	dc_sw	29, MC_DC29_VALUE
+
+	dc_sw	30, MC_DC30_VALUE
+	dc_sw	31, MC_DC31_VALUE
+	dc_sw	32, MC_DC32_VALUE
+	dc_sw	33, MC_DC33_VALUE
+	dc_sw	34, MC_DC34_VALUE
+	dc_sw	35, MC_DC35_VALUE
+	dc_sw	36, MC_DC36_VALUE
+	dc_sw	37, MC_DC37_VALUE
+	dc_sw	38, MC_DC38_VALUE
+	dc_sw	39, MC_DC39_VALUE
+
+	dc_sw	40, MC_DC40_VALUE
+	dc_sw	41, MC_DC41_VALUE
+	dc_sw	42, MC_DC42_VALUE
+	dc_sw	43, MC_DC43_VALUE
+	dc_sw	44, MC_DC44_VALUE
+	dc_sw	45, MC_DC45_VALUE
+	dc_sw	46, MC_DC46_VALUE
+
+	/* Set start bit of DDR memory controller */
+	li	t2, LTQ_MC_DDR_DC03_MC_START
+	sw	t2, LTQ_MC_DDR_DC_OFFSET(3)(t1)
+
+	/* Wait until DLL has locked and core is ready for data transfers */
+wait_ready:
+	lw	t2, LTQ_MC_GEN_STAT(t0)
+	li	t3, LTQ_MC_GEN_STAT_DLCK_PWRON
+	and	t2, t3
+	bne	t2, t3, wait_ready
+
+finished:
+	jr	ra
+
+	END(ltq_mem_init)
--- /dev/null
+++ b/arch/mips/cpu/mips32/danube/pmu.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/lantiq/io.h>
+#include <asm/lantiq/pm.h>
+#include <asm/arch/soc.h>
+
+#define LTQ_PMU_PWDCR_RESERVED		0xFD0C001C
+
+#define LTQ_PMU_PWDCR_TDM		(1 << 25)
+#define LTQ_PMU_PWDCR_PPE_ENET0		(1 << 23)
+#define LTQ_PMU_PWDCR_PPE_ENET1		(1 << 22)
+#define LTQ_PMU_PWDCR_PPE_TC		(1 << 21)
+#define LTQ_PMU_PWDCR_DEU		(1 << 20)
+#define LTQ_PMU_PWDCR_UART1		(1 << 17)
+#define LTQ_PMU_PWDCR_SDIO		(1 << 16)
+#define LTQ_PMU_PWDCR_AHB		(1 << 15)
+#define LTQ_PMU_PWDCR_FPI0		(1 << 14)
+#define LTQ_PMU_PWDCR_PPE		(1 << 13)
+#define LTQ_PMU_PWDCR_GPTC		(1 << 12)
+#define LTQ_PMU_PWDCR_LEDC		(1 << 11)
+#define LTQ_PMU_PWDCR_EBU		(1 << 10)
+#define LTQ_PMU_PWDCR_DSL		(1 << 9)
+#define LTQ_PMU_PWDCR_SPI		(1 << 8)
+#define LTQ_PMU_PWDCR_UART0		(1 << 7)
+#define LTQ_PMU_PWDCR_USB		(1 << 6)
+#define LTQ_PMU_PWDCR_DMA		(1 << 5)
+#define LTQ_PMU_PWDCR_FPI1		(1 << 1)
+#define LTQ_PMU_PWDCR_USB_PHY		(1 << 0)
+
+struct ltq_pmu_regs {
+	u32	rsvd0[7];
+	u32	pwdcr;
+	u32	sr;
+	u32	pwdcr1;
+	u32	sr1;
+};
+
+static struct ltq_pmu_regs *ltq_pmu_regs =
+	(struct ltq_pmu_regs *) CKSEG1ADDR(LTQ_PMU_BASE);
+
+u32 ltq_pm_map(enum ltq_pm_modules module)
+{
+	u32 val;
+
+	switch (module) {
+	case LTQ_PM_CORE:
+		val = LTQ_PMU_PWDCR_UART1 | LTQ_PMU_PWDCR_FPI0 |
+			LTQ_PMU_PWDCR_LEDC | LTQ_PMU_PWDCR_EBU;
+		break;
+	case LTQ_PM_DMA:
+		val = LTQ_PMU_PWDCR_DMA;
+		break;
+	case LTQ_PM_ETH:
+		val = LTQ_PMU_PWDCR_PPE_ENET0 | LTQ_PMU_PWDCR_PPE_TC |
+			LTQ_PMU_PWDCR_PPE;
+		break;
+	case LTQ_PM_SPI:
+		val = LTQ_PMU_PWDCR_SPI;
+		break;
+	default:
+		val = 0;
+		break;
+	}
+
+	return val;
+}
+
+int ltq_pm_enable(enum ltq_pm_modules module)
+{
+	const unsigned long timeout = 1000;
+	unsigned long timebase;
+	u32 sr, val;
+
+	val = ltq_pm_map(module);
+	if (unlikely(!val))
+		return 1;
+
+	ltq_clrbits(&ltq_pmu_regs->pwdcr, val);
+
+	timebase = get_timer(0);
+
+	do {
+		sr = ltq_readl(&ltq_pmu_regs->sr);
+		if (~sr & val)
+			return 0;
+	} while (get_timer(timebase) < timeout);
+
+	return 1;
+}
+
+int ltq_pm_disable(enum ltq_pm_modules module)
+{
+	u32 val;
+
+	val = ltq_pm_map(module);
+	if (unlikely(!val))
+		return 1;
+
+	ltq_setbits(&ltq_pmu_regs->pwdcr, val);
+
+	return 0;
+}
+
+void ltq_pmu_init(void)
+{
+	u32 set, clr;
+
+	clr = ltq_pm_map(LTQ_PM_CORE);
+	set = ~(LTQ_PMU_PWDCR_RESERVED | clr);
+
+	ltq_clrsetbits(&ltq_pmu_regs->pwdcr, clr, set);
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/danube/rcu.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/lantiq/io.h>
+#include <asm/lantiq/reset.h>
+#include <asm/lantiq/cpu.h>
+#include <asm/arch/soc.h>
+
+#define LTQ_RCU_RD_SRST		(1 << 30)	/* Global SW Reset */
+#define LTQ_RCU_RD_MC		(1 << 14)	/* Memory Controller */
+#define LTQ_RCU_RD_PCI		(1 << 13)	/* PCI core */
+#define LTQ_RCU_RD_DFE_AFE	(1 << 12)	/* Voice DFE/AFE */
+#define LTQ_RCU_RD_DSL_AFE	(1 << 11)	/* DSL AFE */
+#define LTQ_RCU_RD_SDIO		(1 << 10)	/* SDIO core */
+#define LTQ_RCU_RD_DMA		(1 << 9)	/* DMA core */
+#define LTQ_RCU_RD_PPE		(1 << 8)	/* PPE core */
+#define LTQ_RCU_RD_ARC_DFE	(1 << 7)	/* ARC/DFE core */
+#define LTQ_RCU_RD_AHB		(1 << 6)	/* AHB bus */
+#define LTQ_RCU_RD_ENET_MAC1	(1 << 5)	/* Ethernet MAC1 */
+#define LTQ_RCU_RD_USB		(1 << 4)	/* USB and Phy core */
+#define LTQ_RCU_RD_CPU1		(1 << 3)	/* CPU1 subsystem */
+#define LTQ_RCU_RD_FPI		(1 << 2)	/* FPI bus */
+#define LTQ_RCU_RD_CPU0		(1 << 1)	/* CPU0 subsystem */
+#define LTQ_RCU_RD_HRST		(1 << 0)	/* HW reset via HRST pin */
+
+#define LTQ_RCU_STAT_BOOT_SHIFT		18
+#define LTQ_RCU_STAT_BOOT_MASK		(0x7 << LTQ_RCU_STAT_BOOT_SHIFT)
+
+struct ltq_rcu_regs {
+	u32	rsvd0[4];
+	u32	req;		/* Reset request */
+	u32	stat;		/* Reset status */
+	u32	usb_cfg;	/* USB configure */
+	u32	rsvd1[2];
+	u32	pci_rdy;	/* PCI boot ready */
+};
+
+static struct ltq_rcu_regs *ltq_rcu_regs =
+	(struct ltq_rcu_regs *) CKSEG1ADDR(LTQ_RCU_BASE);
+
+u32 ltq_reset_map(enum ltq_reset_modules module)
+{
+	u32 val;
+
+	switch (module) {
+	case LTQ_RESET_CORE:
+	case LTQ_RESET_SOFT:
+		val = LTQ_RCU_RD_SRST | LTQ_RCU_RD_CPU1;
+		break;
+	case LTQ_RESET_DMA:
+		val = LTQ_RCU_RD_DMA;
+		break;
+	case LTQ_RESET_ETH:
+		val = LTQ_RCU_RD_PPE;
+		break;
+	case LTQ_RESET_HARD:
+		val = LTQ_RCU_RD_HRST;
+		break;
+	default:
+		val = 0;
+		break;
+	}
+
+	return val;
+}
+
+int ltq_reset_activate(enum ltq_reset_modules module)
+{
+	u32 val;
+
+	val = ltq_reset_map(module);
+	if (unlikely(!val))
+		return 1;
+
+	ltq_setbits(&ltq_rcu_regs->req, val);
+
+	return 0;
+}
+
+int ltq_reset_deactivate(enum ltq_reset_modules module)
+{
+	u32 val;
+
+	val = ltq_reset_map(module);
+	if (unlikely(!val))
+		return 1;
+
+	ltq_clrbits(&ltq_rcu_regs->req, val);
+
+	return 0;
+}
+
+enum ltq_boot_select ltq_boot_select(void)
+{
+	u32 stat;
+	unsigned int bootstrap;
+
+	stat = ltq_readl(&ltq_rcu_regs->stat);
+	bootstrap = (stat & LTQ_RCU_STAT_BOOT_MASK) >> LTQ_RCU_STAT_BOOT_SHIFT;
+
+	switch (bootstrap) {
+	case 0:
+		return BOOT_NOR_NO_BOOTROM;
+	case 1:
+		return BOOT_NOR;
+	case 2:
+		return BOOT_MII0;
+	case 3:
+		return BOOT_PCI;
+	case 4:
+		return BOOT_UART;
+	case 5:
+		return BOOT_SPI;
+	case 6:
+		return BOOT_NAND;
+	case 7:
+		return BOOT_RMII0;
+	default:
+		return BOOT_UNKNOWN;
+	}
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/lantiq-common/Makefile
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2000-2011 Wolfgang Denk, DENX Software Engineering, wd@denx.de
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+include $(TOPDIR)/config.mk
+
+LIB	= $(obj)liblantiq-common.o
+
+START	= start.o
+COBJS-y	= cpu.o pmu.o
+COBJS-$(CONFIG_SPL_BUILD) += spl.o
+SOBJS-y	= lowlevel_init.o
+
+COBJS	:= $(COBJS-y)
+SOBJS	:= $(SOBJS-y)
+SRCS	:= $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c)
+OBJS	:= $(addprefix $(obj),$(SOBJS) $(COBJS))
+START	:= $(addprefix $(obj),$(START))
+
+all:	$(LIB)
+
+$(LIB):	$(obj).depend $(OBJS)
+	$(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
--- /dev/null
+++ b/arch/mips/cpu/mips32/lantiq-common/cpu.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/lantiq/chipid.h>
+#include <asm/lantiq/clk.h>
+#include <asm/lantiq/reset.h>
+#include <asm/lantiq/cpu.h>
+
+static const char ltq_bootsel_strings[][16] = {
+	"NOR",
+	"NOR w/o BootROM",
+	"UART",
+	"UART w/o EEPROM",
+	"SPI",
+	"NAND",
+	"PCI",
+	"MII0",
+	"RMII0",
+	"RGMII1",
+	"unknown",
+};
+
+const char *ltq_boot_select_str(void)
+{	enum ltq_boot_select bootsel = ltq_boot_select();
+
+	if (bootsel > BOOT_UNKNOWN)
+		bootsel = BOOT_UNKNOWN;
+
+	return ltq_bootsel_strings[bootsel];
+}
+
+void ltq_chip_print_info(void)
+{
+	char buf[32];
+
+	printf("SoC:   Lantiq %s v1.%u\n", ltq_chip_partnum_str(),
+		ltq_chip_version_get());
+	printf("CPU:   %s MHz\n", strmhz(buf, ltq_get_cpu_clock()));
+	printf("IO:    %s MHz\n", strmhz(buf, ltq_get_io_region_clock()));
+	printf("BUS:   %s MHz\n", strmhz(buf, ltq_get_bus_clock()));
+	printf("BOOT:  %s\n", ltq_boot_select_str());
+}
+
+int arch_cpu_init(void)
+{
+	ltq_pmu_init();
+	ltq_ebu_init();
+
+	return 0;
+}
+
+void _machine_restart(void)
+{
+	ltq_reset_activate(LTQ_RESET_CORE);
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/lantiq-common/lowlevel_init.S
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <asm/asm.h>
+#include <asm/regdef.h>
+
+NESTED(lowlevel_init, 0, ra)
+	move	t8, ra
+
+	la	t7, ltq_cgu_init
+	jalr	t7
+
+	la	t7, ltq_mem_init
+	jalr	t7
+
+	jr	t8
+	END(lowlevel_init)
--- /dev/null
+++ b/arch/mips/cpu/mips32/lantiq-common/pmu.c
@@ -0,0 +1,9 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/lantiq/pm.h>
+
--- /dev/null
+++ b/arch/mips/cpu/mips32/lantiq-common/spl.c
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <image.h>
+#include <version.h>
+#include <spi_flash.h>
+#include <linux/compiler.h>
+#include <lzma/LzmaDec.h>
+#include <linux/lzo.h>
+#include <asm/mipsregs.h>
+
+#if defined(CONFIG_LTQ_SPL_CONSOLE)
+#define spl_has_console		1
+
+#if defined(CONFIG_LTQ_SPL_DEBUG)
+#define spl_has_debug		1
+#else
+#define spl_has_debug		0
+#endif
+
+#else
+#define spl_has_console		0
+#define spl_has_debug		0
+#endif
+
+#define spl_debug(fmt, args...)			\
+	do {					\
+		if (spl_has_debug)		\
+			printf(fmt, ##args);	\
+	} while (0)
+
+#define spl_puts(msg)				\
+	do {					\
+		if (spl_has_console)		\
+			puts(msg);		\
+	} while (0)
+
+#if defined(CONFIG_LTQ_SUPPORT_SPL_SPI_FLASH) && defined(CONFIG_SYS_BOOT_SFSPL)
+#define spl_boot_spi_flash	1
+#else
+#define spl_boot_spi_flash	0
+#ifndef CONFIG_SPL_SPI_BUS
+#define CONFIG_SPL_SPI_BUS	0
+#endif
+#ifndef CONFIG_SPL_SPI_CS
+#define CONFIG_SPL_SPI_CS	0
+#endif
+#ifndef CONFIG_SPL_SPI_MAX_HZ
+#define CONFIG_SPL_SPI_MAX_HZ	0
+#endif
+#ifndef CONFIG_SPL_SPI_MODE
+#define CONFIG_SPL_SPI_MODE	0
+#endif
+#endif
+
+#if defined(CONFIG_LTQ_SUPPORT_SPL_NOR_FLASH) && defined(CONFIG_SYS_BOOT_NORSPL)
+#define spl_boot_nor_flash	1
+#else
+#define spl_boot_nor_flash	0
+#endif
+
+#define spl_sync()	__asm__ __volatile__("sync");
+
+struct spl_image {
+	ulong data_addr;
+	ulong entry_addr;
+	ulong data_size;
+	ulong entry_size;
+	ulong data_crc;
+	u8 comp;
+};
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Emulated malloc area needed for LZMA allocator in BSS */
+static u8 *spl_mem_ptr __maybe_unused;
+static size_t spl_mem_size __maybe_unused;
+
+static int spl_is_comp_lzma(const struct spl_image *spl)
+{
+#if defined(CONFIG_LTQ_SPL_COMP_LZMA)
+	return spl->comp == IH_COMP_LZMA;
+#else
+	return 0;
+#endif
+}
+
+static int spl_is_comp_lzo(const struct spl_image *spl)
+{
+#if defined(CONFIG_LTQ_SPL_COMP_LZO)
+	return spl->comp == IH_COMP_LZO;
+#else
+	return 0;
+#endif
+}
+
+static int spl_is_compressed(const struct spl_image *spl)
+{
+	if (spl_is_comp_lzma(spl))
+		return 1;
+
+	if (spl_is_comp_lzo(spl))
+		return 1;
+
+	return 0;
+}
+
+static void spl_console_init(void)
+{
+	if (!spl_has_console)
+		return;
+
+	gd->flags |= GD_FLG_RELOC;
+	gd->baudrate = CONFIG_BAUDRATE;
+
+	serial_init();
+
+	gd->have_console = 1;
+
+	spl_puts("\nU-Boot SPL " PLAIN_VERSION " (" U_BOOT_DATE " - " \
+		U_BOOT_TIME ")\n");
+}
+
+static int spl_parse_image(const image_header_t *hdr, struct spl_image *spl)
+{
+	spl_puts("SPL: checking U-Boot image\n");
+
+	if (!image_check_magic(hdr)) {
+		spl_puts("SPL: invalid magic\n");
+		return -1;
+	}
+
+        if (!image_check_hcrc(hdr)) {
+		spl_puts("SPL: invalid header CRC\n");
+		return -1;
+	}
+
+	spl->data_addr += image_get_header_size();
+	spl->entry_addr = image_get_load(hdr);
+	spl->data_size = image_get_data_size(hdr);
+	spl->data_crc = image_get_dcrc(hdr);
+	spl->comp = image_get_comp(hdr);
+
+	spl_debug("SPL: data %08lx, size %lu, entry %08lx, comp %u\n",
+		spl->data_addr, spl->data_size, spl->entry_addr, spl->comp);
+
+	return 0;
+}
+
+static int spl_check_data(const struct spl_image *spl, ulong loadaddr)
+{
+	ulong dcrc = crc32(0, (unsigned char *)loadaddr, spl->data_size);
+
+	if (dcrc != spl->data_crc) {
+		spl_puts("SPL: invalid data CRC\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+static void *spl_lzma_alloc(void *p, size_t size)
+{
+	u8 *ret;
+
+	if (size > spl_mem_size)
+		return NULL;
+
+	ret = spl_mem_ptr;
+	spl_mem_ptr += size;
+	spl_mem_size -= size;
+
+	return ret;
+}
+
+static void spl_lzma_free(void *p, void *addr)
+{
+}
+
+static int spl_copy_image(struct spl_image *spl)
+{
+	spl_puts("SPL: copying U-Boot to RAM\n");
+
+	memcpy((void *) spl->entry_addr, (const void *) spl->data_addr,
+		spl->data_size);
+
+	spl->entry_size = spl->data_size;
+
+	return 0;
+}
+
+static int spl_uncompress_lzma(struct spl_image *spl, unsigned long loadaddr)
+{
+	SRes res;
+	const Byte *prop = (const Byte *) loadaddr;
+	const Byte *src = (const Byte *) loadaddr + LZMA_PROPS_SIZE +
+							sizeof(uint64_t);
+	Byte *dest = (Byte *) spl->entry_addr;
+	SizeT dest_len = 0xFFFFFFFF;
+	SizeT src_len = spl->data_size - LZMA_PROPS_SIZE;
+	ELzmaStatus status = 0;
+	ISzAlloc alloc;
+
+	spl_puts("SPL: decompressing U-Boot with LZMA\n");
+
+	alloc.Alloc = spl_lzma_alloc;
+	alloc.Free = spl_lzma_free;
+	spl_mem_ptr = (u8 *) CONFIG_SPL_MALLOC_BASE;
+	spl_mem_size = CONFIG_SPL_MALLOC_MAX_SIZE;
+
+	res = LzmaDecode(dest, &dest_len, src, &src_len, prop, LZMA_PROPS_SIZE,
+		LZMA_FINISH_ANY, &status, &alloc);
+	if (res != SZ_OK)
+		return 1;
+
+	spl->entry_size = dest_len;
+
+	return 0;
+}
+
+static int spl_uncompress_lzo(struct spl_image *spl, unsigned long loadaddr)
+{
+	size_t len = CONFIG_SYS_LOAD_SIZE;
+	int ret;
+
+	spl_puts("SPL: decompressing U-Boot with LZO\n");
+
+	ret = lzop_decompress(
+		(const unsigned char*) loadaddr, spl->data_size,
+		(unsigned char *) spl->entry_addr, &len);
+
+	spl->entry_size = len;
+
+	return ret;
+}
+
+static int spl_uncompress(struct spl_image *spl, unsigned long loadaddr)
+{
+	int ret;
+
+	if (spl_is_comp_lzma(spl))
+		ret = spl_uncompress_lzma(spl, loadaddr);
+	else if (spl_is_comp_lzo(spl))
+		ret = spl_uncompress_lzo(spl, loadaddr);
+	else
+		ret = 1;
+
+	return ret;
+}
+
+static int spl_load_spi_flash(struct spl_image *spl)
+{
+	struct spi_flash sf = { 0 };
+	image_header_t hdr;
+	int ret;
+	unsigned long loadaddr;
+
+	/*
+	 * Image format:
+	 *
+	 * - 12 byte non-volatile bootstrap header
+	 * - SPL binary
+	 * - 12 byte non-volatile bootstrap header
+	 * - 64 byte U-Boot mkimage header
+	 * - U-Boot binary
+	 */
+	spl->data_addr = image_copy_end() - CONFIG_SPL_TEXT_BASE + 24;
+
+	spl_puts("SPL: probing SPI flash\n");
+
+	spi_init();
+	ret = spl_spi_flash_probe(&sf);
+	if (ret)
+		return ret;
+
+	spl_debug("SPL: reading image header at offset %lx\n", spl->data_addr);
+
+	ret = spi_flash_read(&sf, spl->data_addr, sizeof(hdr), &hdr);
+	if (ret)
+		return ret;
+
+	spl_debug("SPL: checking image header at offset %lx\n", spl->data_addr);
+
+	ret = spl_parse_image(&hdr, spl);
+	if (ret)
+		return ret;
+
+	if (spl_is_compressed(spl))
+		loadaddr = CONFIG_LOADADDR;
+	else
+		loadaddr = spl->entry_addr;
+
+	spl_puts("SPL: loading U-Boot to RAM\n");
+
+	ret = spi_flash_read(&sf, spl->data_addr, spl->data_size,
+				(void *) loadaddr);
+
+	if (!spl_check_data(spl, loadaddr))
+		return -1;
+
+	if (spl_is_compressed(spl))
+		ret = spl_uncompress(spl, loadaddr);
+
+	return ret;
+}
+
+static int spl_load_nor_flash(struct spl_image *spl)
+{
+	const image_header_t *hdr;
+	int ret;
+
+	/*
+	 * Image format:
+	 *
+	 * - SPL binary
+	 * - 64 byte U-Boot mkimage header
+	 * - U-Boot binary
+	 */
+	spl->data_addr = image_copy_end();
+	hdr = (const image_header_t *) image_copy_end();
+
+	spl_debug("SPL: checking image header at address %p\n", hdr);
+
+	ret = spl_parse_image(hdr, spl);
+	if (ret)
+		return ret;
+
+	if (spl_is_compressed(spl))
+		ret = spl_uncompress(spl, spl->data_addr);
+	else
+		ret = spl_copy_image(spl);
+
+	return ret;
+}
+
+static int spl_load(struct spl_image *spl)
+{
+	int ret;
+
+	if (spl_boot_spi_flash)
+		ret = spl_load_spi_flash(spl);
+	else if (spl_boot_nor_flash)
+		ret = spl_load_nor_flash(spl);
+	else
+		ret = 1;
+
+	return ret;
+}
+
+void __noreturn spl_lantiq_init(void)
+{
+	void (*uboot)(void) __noreturn;
+	struct spl_image spl;
+	gd_t gd_data;
+	int ret;
+
+	gd = &gd_data;
+	barrier();
+	memset((void *)gd, 0, sizeof(gd_t));
+
+	spl_console_init();
+
+	spl_debug("SPL: initializing\n");
+
+#if 0
+	spl_debug("CP0_CONFIG:   %08x\n", read_c0_config());
+	spl_debug("CP0_CONFIG1:  %08x\n", read_c0_config1());
+	spl_debug("CP0_CONFIG2:  %08x\n", read_c0_config2());
+	spl_debug("CP0_CONFIG3:  %08x\n", read_c0_config3());
+	spl_debug("CP0_CONFIG6:  %08x\n", read_c0_config6());
+	spl_debug("CP0_CONFIG7:  %08x\n", read_c0_config7());
+	spl_debug("CP0_STATUS:   %08x\n", read_c0_status());
+	spl_debug("CP0_PRID:     %08x\n", read_c0_prid());
+#endif
+
+	board_early_init_f();
+	timer_init();
+
+	memset(&spl, 0, sizeof(spl));
+
+	ret = spl_load(&spl);
+	if (ret)
+		goto hang;
+
+	spl_debug("SPL: U-Boot entry %08lx\n", spl.entry_addr);
+	spl_puts("SPL: jumping to U-Boot\n");
+
+	flush_cache(spl.entry_addr, spl.entry_size);
+	spl_sync();
+
+	uboot = (void *) spl.entry_addr;
+	uboot();
+
+hang:
+	spl_puts("SPL: cannot start U-Boot\n");
+
+	for (;;)
+		;
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/lantiq-common/start.S
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <config.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+
+#define S_PRIdCoID	16		/* Company ID (R) */
+#define M_PRIdCoID	(0xff << S_PRIdCoID)
+#define S_PRIdImp	8		/* Implementation ID (R) */
+#define M_PRIdImp	(0xff << S_PRIdImp)
+
+#define K_CacheAttrCWTnWA	0	/* Cacheable, write-thru, no write allocate */
+#define K_CacheAttrCWTWA	1	/* Cacheable, write-thru, write allocate */
+#define K_CacheAttrU		2	/* Uncached */
+#define K_CacheAttrC		3	/* Cacheable */
+#define K_CacheAttrCN		3	/* Cacheable, non-coherent */
+#define K_CacheAttrCCE		4	/* Cacheable, coherent, exclusive */
+#define K_CacheAttrCCS		5	/* Cacheable, coherent, shared */
+#define K_CacheAttrCCU		6	/* Cacheable, coherent, update */
+#define K_CacheAttrUA		7	/* Uncached accelerated */
+
+#define S_ConfigK23		28	/* Kseg2/3 coherency algorithm (FM MMU only) (R/W) */
+#define M_ConfigK23		(0x7 << S_ConfigK23)
+#define W_ConfigK23		3
+#define S_ConfigKU		25	/* Kuseg coherency algorithm (FM MMU only) (R/W) */
+#define M_ConfigKU		(0x7 << S_ConfigKU)
+#define W_ConfigKU		3
+
+#define S_ConfigMM		18	/* Merge mode (implementation specific) */
+#define M_ConfigMM		(0x1 << S_ConfigMM)
+
+#define S_StatusBEV		22	/* Enable Boot Exception Vectors (R/W) */
+#define M_StatusBEV		(0x1 << S_StatusBEV)
+
+#define S_StatusFR		26	/* Enable 64-bit FPRs (R/W) */
+#define M_StatusFR		(0x1 << S_StatusFR)
+
+#define S_ConfigK0		0	/* Kseg0 coherency algorithm (R/W) */
+#define M_ConfigK0		(0x7 << S_ConfigK0)
+
+#define CONFIG0_MIPS32_64_MSK	0x8000ffff
+#define STATUS_MIPS32_64_MSK	0xfffcffff
+
+#define STATUS_MIPS24K		0
+#define CONFIG0_MIPS24K		((K_CacheAttrCN << S_ConfigK23) |\
+				(K_CacheAttrCN << S_ConfigKU)  |\
+				(M_ConfigMM))
+
+#define STATUS_MIPS34K		0
+#define CONFIG0_MIPS34K		((K_CacheAttrCN << S_ConfigK23) |\
+				(K_CacheAttrCN << S_ConfigKU) |\
+				(M_ConfigMM))
+
+#define STATUS_MIPS32_64	(M_StatusBEV | M_StatusFR)
+#define CONFIG0_MIPS32_64	(K_CacheAttrCN << S_ConfigK0)
+
+#ifdef CONFIG_SOC_XWAY_DANUBE
+#define CONFIG0_LANTIQ		(CONFIG0_MIPS24K | CONFIG0_MIPS32_64)
+#define STATUS_LANTIQ		(STATUS_MIPS24K | STATUS_MIPS32_64)
+#endif
+
+#ifdef CONFIG_SOC_XWAY_VRX200
+#define CONFIG0_LANTIQ		(CONFIG0_MIPS34K | CONFIG0_MIPS32_64)
+#define STATUS_LANTIQ		(STATUS_MIPS34K | STATUS_MIPS32_64)
+#endif
+
+
+	.set noreorder
+
+	.globl _start
+	.text
+_start:
+	/* Entry point */
+	b	main
+	 nop
+
+	/* Lantiq SoC Boot config word */
+	.org	0x10
+#ifdef CONFIG_SYS_XWAY_EBU_BOOTCFG
+	.word	CONFIG_SYS_XWAY_EBU_BOOTCFG
+#else
+	.word	0
+#endif
+	.word	0
+
+	.align	4
+main:
+
+	/* Init Timer */
+	mtc0	zero, CP0_COUNT
+	mtc0	zero, CP0_COMPARE
+
+	/* Setup MIPS24K/MIPS34K specifics (implementation dependent fields) */
+	mfc0	t0, CP0_CONFIG
+	li	t1, CONFIG0_MIPS32_64_MSK
+	and	t0, t1
+	li	t1, CONFIG0_LANTIQ
+	or	t0, t1
+	mtc0	t0, CP0_CONFIG
+
+	mfc0	t0, CP0_STATUS
+	li	t1, STATUS_MIPS32_64_MSK
+	and	t0, t1
+	li	t1, STATUS_LANTIQ
+	or	t0, t1
+	mtc0	t0, CP0_STATUS
+
+	/* Initialize CGU */
+	la	t9, ltq_cgu_init
+	jalr	t9
+	 nop
+
+	/* Initialize memory controller */
+	la	t9, ltq_mem_init
+	jalr	t9
+	 nop
+
+	/* Initialize caches... */
+	la	t9, mips_cache_reset
+	jalr	t9
+	 nop
+
+	/* Clear BSS */
+	la	t1, __bss_start
+	la	t2, __bss_end
+	sub	t1, 4
+1:
+	addi	t1, 4
+	bltl	t1, t2, 1b
+	 sw	zero, 0(t1)
+
+	/* Setup stack pointer and force alignment on a 16 byte boundary */
+	li	t0, (CONFIG_SPL_STACK_BASE & ~0xF)
+	la	sp, 0(t0)
+
+	la	t9, spl_lantiq_init
+	jr	t9
+	 nop
--- /dev/null
+++ b/arch/mips/cpu/mips32/lantiq-common/u-boot-spl.lds
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+MEMORY { .spl_mem : ORIGIN = CONFIG_SPL_TEXT_BASE, \
+		LENGTH = CONFIG_SPL_MAX_SIZE }
+MEMORY { .bss_mem : ORIGIN = CONFIG_SPL_BSS_BASE, \
+		LENGTH = CONFIG_SPL_BSS_MAX_SIZE }
+
+OUTPUT_FORMAT("elf32-tradbigmips", "elf32-tradbigmips", "elf32-tradlittlemips")
+OUTPUT_ARCH(mips)
+ENTRY(_start)
+SECTIONS
+{
+	. = ALIGN(4);
+	.text : {
+		*(.text*)
+	} > .spl_mem
+
+	. = ALIGN(4);
+	.rodata : {
+		*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
+	} > .spl_mem
+
+	. = ALIGN(4);
+	.data : {
+		*(SORT_BY_ALIGNMENT(.data*))
+		*(SORT_BY_ALIGNMENT(.sdata*))
+	} > .spl_mem
+
+	. = ALIGN(4);
+	__image_copy_end = .;
+	uboot_end_data = .;
+
+	.bss : {
+		__bss_start = .;
+		*(.bss*)
+		*(.sbss*)
+		. = ALIGN(4);
+		__bss_end = .;
+	} > .bss_mem
+
+	. = ALIGN(4);
+	__end = .;
+	uboot_end = .;
+}
--- a/arch/mips/cpu/mips32/start.S
+++ b/arch/mips/cpu/mips32/start.S
@@ -105,7 +105,7 @@ reset:
 	mtc0	zero, CP0_COUNT
 	mtc0	zero, CP0_COMPARE
 
-#ifndef CONFIG_SKIP_LOWLEVEL_INIT
+#if !defined(CONFIG_SKIP_LOWLEVEL_INIT) || defined(CONFIG_SYS_DISABLE_CACHE)
 	/* CONFIG0 register */
 	li	t0, CONF_CM_UNCACHED
 	mtc0	t0, CP0_CONFIG
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/Makefile
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2000-2011 Wolfgang Denk, DENX Software Engineering, wd@denx.de
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+include $(TOPDIR)/config.mk
+
+LIB	= $(obj)lib$(SOC).o
+
+COBJS-y	+= cgu.o chipid.o dcdc.o ebu.o gphy.o mem.o pmu.o rcu.o
+SOBJS-y	+= cgu_init.o mem_init.o
+SOBJS-y	+= gphy_fw.o
+
+COBJS	:= $(COBJS-y)
+SOBJS	:= $(SOBJS-y)
+SRCS	:= $(SOBJS:.o=.S) $(COBJS:.o=.c)
+OBJS	:= $(addprefix $(obj),$(SOBJS) $(COBJS))
+
+all:	$(LIB)
+
+$(LIB):	$(obj).depend $(OBJS)
+	$(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/cgu.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/soc.h>
+#include <asm/arch/gphy.h>
+#include <asm/lantiq/clk.h>
+#include <asm/lantiq/io.h>
+
+#define LTQ_CGU_PLL1_PLLN_SHIFT		6
+#define LTQ_CGU_PLL1_PLLN_MASK		(0x3F << LTQ_CGU_PLL1_PLLN_SHIFT)
+#define LTQ_CGU_PLL1_PLLM_SHIFT		2
+#define LTQ_CGU_PLL1_PLLM_MASK		(0xF << LTQ_CGU_PLL1_PLLM_SHIFT)
+#define LTQ_CGU_PLL1_PLLL		(1 << 1)
+#define LTQ_CGU_PLL1_PLL_EN		1
+
+#define LTQ_CGU_SYS_OCP_SHIFT		0
+#define LTQ_CGU_SYS_OCP_MASK		(0x3 << LTQ_CGU_SYS_OCP_SHIFT)
+#define LTQ_CGU_SYS_CPU_SHIFT		4
+#define LTQ_CGU_SYS_CPU_MASK		(0xF << LTQ_CGU_SYS_CPU_SHIFT)
+
+#define LTQ_CGU_UPDATE			1
+
+#define LTQ_CGU_IFCLK_GPHY_SEL_SHIFT	2
+#define LTQ_CGU_IFCLK_GPHY_SEL_MASK	(0x7 << LTQ_CGU_IFCLK_GPHY_SEL_SHIFT)
+
+struct ltq_cgu_regs {
+	u32	rsvd0;
+	u32	pll0_cfg;	/* PLL0 config */
+	u32	pll1_cfg;	/* PLL1 config */
+	u32	sys;		/* System clock */
+	u32	clk_fsr;	/* Clock frequency select */
+	u32	clk_gsr;	/* Clock gating status */
+	u32	clk_gcr0;	/* Clock gating control 0 */
+	u32	clk_gcr1;	/* Clock gating control 1 */
+	u32	update;		/* CGU update control */
+	u32	if_clk;		/* Interface clock */
+	u32	ddr;		/* DDR memory control */
+	u32	ct1_sr;		/* CT status 1 */
+	u32	ct_kval;	/* CT K value */
+	u32	pcm_cr;		/* PCM control */
+	u32	pci_cr;		/* PCI clock control */
+	u32	rsvd1;
+	u32	gphy1_cfg;	/* GPHY1 config */
+	u32	gphy0_cfg;	/* GPHY0 config */
+	u32	rsvd2[6];
+	u32	pll2_cfg;	/* PLL2 config */
+};
+
+static struct ltq_cgu_regs *ltq_cgu_regs =
+	(struct ltq_cgu_regs *) CKSEG1ADDR(LTQ_CGU_BASE);
+
+static inline u32 ltq_cgu_sys_readl(u32 mask, u32 shift)
+{
+	return (ltq_readl(&ltq_cgu_regs->sys) & mask) >> shift;
+}
+
+unsigned long ltq_get_io_region_clock(void)
+{
+	unsigned int ocp_sel;
+	unsigned long clk, cpu_clk;
+
+	cpu_clk = ltq_get_cpu_clock();
+
+	ocp_sel = ltq_cgu_sys_readl(LTQ_CGU_SYS_OCP_MASK,
+			LTQ_CGU_SYS_OCP_SHIFT);
+
+	switch (ocp_sel) {
+	case 0:
+		/* OCP ratio 1 */
+		clk = cpu_clk;
+		break;
+	case 2:
+		/* OCP ratio 2 */
+		clk = cpu_clk / 2;
+		break;
+	case 3:
+		/* OCP ratio 2.5 */
+		clk = (cpu_clk * 2) / 5;
+		break;
+	case 4:
+		/* OCP ratio 3 */
+		clk = cpu_clk / 3;
+		break;
+	default:
+		clk = 0;
+		break;
+	}
+
+	return clk;
+}
+
+unsigned long ltq_get_cpu_clock(void)
+{
+	unsigned int cpu_sel;
+	unsigned long clk;
+
+	cpu_sel = ltq_cgu_sys_readl(LTQ_CGU_SYS_CPU_MASK,
+			LTQ_CGU_SYS_CPU_SHIFT);
+
+	switch (cpu_sel) {
+	case 0:
+		clk = CLOCK_600_MHZ;
+		break;
+	case 1:
+		clk = CLOCK_500_MHZ;
+		break;
+	case 2:
+		clk = CLOCK_393_MHZ;
+		break;
+	case 3:
+		clk = CLOCK_333_MHZ;
+		break;
+	case 5:
+	case 6:
+		clk = CLOCK_197_MHZ;
+		break;
+	case 7:
+		clk = CLOCK_166_MHZ;
+		break;
+	case 4:
+	case 8:
+	case 9:
+		clk = CLOCK_125_MHZ;
+		break;
+	default:
+		clk = 0;
+		break;
+	}
+
+	return clk;
+}
+
+unsigned long ltq_get_bus_clock(void)
+{
+	return ltq_get_io_region_clock();
+}
+
+void ltq_cgu_gphy_clk_src(enum ltq_gphy_clk clk)
+{
+	ltq_clrbits(&ltq_cgu_regs->if_clk, LTQ_CGU_IFCLK_GPHY_SEL_MASK);
+	ltq_setbits(&ltq_cgu_regs->if_clk, clk << LTQ_CGU_IFCLK_GPHY_SEL_SHIFT);
+}
+
+static inline int ltq_cgu_pll1_locked(void)
+{
+	u32 pll1_cfg = ltq_readl(&ltq_cgu_regs->pll1_cfg);
+
+	return pll1_cfg & LTQ_CGU_PLL1_PLLL;
+}
+
+static inline void ltq_cgu_pll1_restart(unsigned m, unsigned n)
+{
+	u32 pll1_cfg;
+
+	ltq_clrbits(&ltq_cgu_regs->pll1_cfg, LTQ_CGU_PLL1_PLL_EN);
+	ltq_setbits(&ltq_cgu_regs->update, LTQ_CGU_UPDATE);
+
+	pll1_cfg = ltq_readl(&ltq_cgu_regs->pll1_cfg);
+	pll1_cfg &= ~(LTQ_CGU_PLL1_PLLN_MASK | LTQ_CGU_PLL1_PLLM_MASK);
+	pll1_cfg |= n << LTQ_CGU_PLL1_PLLN_SHIFT;
+	pll1_cfg |= m << LTQ_CGU_PLL1_PLLM_SHIFT;
+	pll1_cfg |= LTQ_CGU_PLL1_PLL_EN;
+	ltq_writel(&ltq_cgu_regs->pll1_cfg, pll1_cfg);
+	ltq_setbits(&ltq_cgu_regs->update, LTQ_CGU_UPDATE);
+
+	__udelay(1000);
+}
+
+/*
+ * From chapter 9 in errata sheet:
+ *
+ * Under certain condition, the PLL1 may failed to enter into lock
+ * status by hardware default N, M setting.
+ *
+ * Since system always starts from PLL0, the system software can run
+ * and re-program the PLL1 settings.
+ */
+static void ltq_cgu_pll1_init(void)
+{
+	unsigned i;
+	const unsigned pll1_m[] = { 1, 2, 3, 4 };
+	const unsigned pll1_n[] = { 21, 32, 43, 54 };
+
+	/* Check if PLL1 has locked with hardware default settings */
+	if (ltq_cgu_pll1_locked())
+		return;
+
+	for (i = 0; i < 4; i++) {
+		ltq_cgu_pll1_restart(pll1_m[i], pll1_n[i]);
+
+		if (ltq_cgu_pll1_locked())
+			goto done;
+	}
+
+done:
+	/* Restart with hardware default values M=5, N=64 */
+	ltq_cgu_pll1_restart(5, 64);
+}
+
+void ltq_pll_init(void)
+{
+	ltq_cgu_pll1_init();
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/cgu_init.S
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <config.h>
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/addrspace.h>
+#include <asm/arch/soc.h>
+
+/* RCU module register */
+#define LTQ_RCU_RST_REQ			0x0010	/* Reset request */
+#define LTQ_RCU_RST_REQ_VALUE		((1 << 14) | (1 << 1))
+
+/* CGU module register */
+#define LTQ_CGU_PLL0_CFG		0x0004	/* PLL0 config */
+#define LTQ_CGU_PLL1_CFG		0x0008	/* PLL1 config */
+#define LTQ_CGU_PLL2_CFG		0x0060	/* PLL2 config */
+#define LTQ_CGU_SYS			0x000C	/* System clock */
+#define LTQ_CGU_CLK_FSR			0x0010	/* Clock frequency select */
+#define LTQ_CGU_UPDATE			0x0020	/* Clock update control */
+
+/* Valid SYS.CPU values */
+#define LTQ_CGU_SYS_CPU_SHIFT		4
+#define LTQ_CGU_SYS_CPU_600_MHZ		0x0
+#define LTQ_CGU_SYS_CPU_500_MHZ		0x1
+#define LTQ_CGU_SYS_CPU_393_MHZ		0x2
+#define LTQ_CGU_SYS_CPU_333_MHZ		0x3
+#define LTQ_CGU_SYS_CPU_197_MHZ		0x5
+#define LTQ_CGU_SYS_CPU_166_MHZ		0x7
+#define LTQ_CGU_SYS_CPU_125_MHZ		0x9
+
+/* Valid SYS.OCP values */
+#define LTQ_CGU_SYS_OCP_SHIFT		0
+#define LTQ_CGU_SYS_OCP_1		0x0
+#define LTQ_CGU_SYS_OCP_2		0x2
+#define LTQ_CGU_SYS_OCP_2_5		0x3
+#define LTQ_CGU_SYS_OCP_3		0x4
+
+/* Valid CLK_FSR.ETH values */
+#define LTQ_CGU_CLK_FSR_ETH_SHIFT	24
+#define LTQ_CGU_CLK_FSR_ETH_50_MHZ	0x0
+#define LTQ_CGU_CLK_FSR_ETH_25_MHZ	0x1
+#define LTQ_CGU_CLK_FSR_ETH_2_5_MHZ	0x2
+#define LTQ_CGU_CLK_FSR_ETH_125_MHZ	0x3
+
+/* Valid CLK_FSR.PPE values */
+#define LTQ_CGU_CLK_FSR_PPE_SHIFT	16
+#define LTQ_CGU_CLK_FSR_PPE_500_MHZ	0x0	/* Overclock frequency */
+#define LTQ_CGU_CLK_FSR_PPE_450_MHZ	0x1	/* High frequency */
+#define LTQ_CGU_CLK_FSR_PPE_400_MHZ	0x2	/* Low frequency */
+
+#if (CONFIG_SYS_CLOCK_MODE == LTQ_CLK_CPU_500_DDR_250)
+#define LTQ_CGU_SYS_CPU_CONFIG		LTQ_CGU_SYS_CPU_500_MHZ
+#define LTQ_CGU_SYS_OCP_CONFIG		LTQ_CGU_SYS_OCP_2
+#define LTQ_CGU_CLK_FSR_ETH_CONFIG	LTQ_CGU_CLK_FSR_ETH_125_MHZ
+#define LTQ_CGU_CLK_FSR_PPE_CONFIG	LTQ_CGU_CLK_FSR_PPE_450_MHZ
+#else
+#error "Invalid system clock configuration!"
+#endif
+
+/* Build register values */
+#define LTQ_CGU_SYS_VALUE	((LTQ_CGU_SYS_CPU_CONFIG << \
+					LTQ_CGU_SYS_CPU_SHIFT) | \
+					LTQ_CGU_SYS_OCP_CONFIG)
+
+#define LTQ_CGU_CLK_FSR_VALUE	((LTQ_CGU_CLK_FSR_ETH_CONFIG << \
+					LTQ_CGU_CLK_FSR_ETH_SHIFT) | \
+				(LTQ_CGU_CLK_FSR_PPE_CONFIG << \
+					LTQ_CGU_CLK_FSR_PPE_SHIFT))
+
+	.set noreorder
+
+LEAF(ltq_cgu_init)
+	/* Load current CGU register values */
+	li	t0, (LTQ_CGU_BASE | KSEG1)
+	lw	t1, LTQ_CGU_SYS(t0)
+	lw	t2, LTQ_CGU_CLK_FSR(t0)
+
+	/* Load target CGU register values */
+	li	t3, LTQ_CGU_SYS_VALUE
+	li	t4, LTQ_CGU_CLK_FSR_VALUE
+
+	/* Only update registers if values differ */
+	bne	t1, t3, update
+	 nop
+	beq	t2, t4, finished
+	 nop
+
+update:
+	/* Store target register values */
+	sw	t3, LTQ_CGU_SYS(t0)
+	sw	t4, LTQ_CGU_CLK_FSR(t0)
+
+	/* Perform software reset to activate new clock config */
+#if 0
+	li	t0, (LTQ_RCU_BASE | KSEG1)
+	lw	t1, LTQ_RCU_RST_REQ(t0)
+	or	t1, LTQ_RCU_RST_REQ_VALUE
+	sw	t1, LTQ_RCU_RST_REQ(t0)
+#else
+	li	t1, 1
+	sw	t1, LTQ_CGU_UPDATE(t0)
+#endif
+
+#if 0
+wait_reset:
+	b	wait_reset
+	 nop
+#endif
+
+finished:
+	jr	ra
+	 nop
+
+	END(ltq_cgu_init)
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/chipid.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/lantiq/io.h>
+#include <asm/lantiq/chipid.h>
+#include <asm/arch/soc.h>
+
+#define LTQ_CHIPID_VERSION_SHIFT	28
+#define LTQ_CHIPID_VERSION_MASK		(0x7 << LTQ_CHIPID_VERSION_SHIFT)
+#define LTQ_CHIPID_PNUM_SHIFT		12
+#define LTQ_CHIPID_PNUM_MASK		(0xFFFF << LTQ_CHIPID_PNUM_SHIFT)
+
+struct ltq_chipid_regs {
+	u32	manid;		/* Manufacturer identification */
+	u32	chipid;		/* Chip identification */
+};
+
+static struct ltq_chipid_regs *ltq_chipid_regs =
+	(struct ltq_chipid_regs *) CKSEG1ADDR(LTQ_CHIPID_BASE);
+
+unsigned int ltq_chip_version_get(void)
+{
+	u32 chipid;
+
+	chipid = ltq_readl(&ltq_chipid_regs->chipid);
+
+	return (chipid & LTQ_CHIPID_VERSION_MASK) >> LTQ_CHIPID_VERSION_SHIFT;
+}
+
+unsigned int ltq_chip_partnum_get(void)
+{
+	u32 chipid;
+
+	chipid = ltq_readl(&ltq_chipid_regs->chipid);
+
+	return (chipid & LTQ_CHIPID_PNUM_MASK) >> LTQ_CHIPID_PNUM_SHIFT;
+}
+
+const char *ltq_chip_partnum_str(void)
+{
+	enum ltq_chip_partnum partnum = ltq_chip_partnum_get();
+
+	switch (partnum) {
+	case LTQ_SOC_VRX268:
+	case LTQ_SOC_VRX268_2:
+		return "VRX268";
+	case LTQ_SOC_VRX288:
+	case LTQ_SOC_VRX288_2:
+		return "VRX288";
+	case LTQ_SOC_GRX288:
+	case LTQ_SOC_GRX288_2:
+		return "GRX288";
+	default:
+		printf("Unknown partnum: %x\n", partnum);
+	}
+
+	return "";
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/config.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+PF_CPPFLAGS_XRX := $(call cc-option,-mtune=34kc,)
+PLATFORM_CPPFLAGS += $(PF_CPPFLAGS_XRX)
+
+ifdef CONFIG_SPL_BUILD
+PF_ABICALLS		:= -mno-abicalls
+PF_PIC			:= -fno-pic
+PF_PIE			:=
+USE_PRIVATE_LIBGCC	:= yes
+endif
+
+LIBS-y += $(CPUDIR)/lantiq-common/liblantiq-common.o
+
+ifndef CONFIG_SPL_BUILD
+ifdef CONFIG_SYS_BOOT_SFSPL
+ALL-y += $(obj)u-boot.ltq.sfspl
+ALL-$(CONFIG_SPL_LZO_SUPPORT) += $(obj)u-boot.ltq.lzo.sfspl
+ALL-$(CONFIG_SPL_LZMA_SUPPORT) += $(obj)u-boot.ltq.lzma.sfspl
+endif
+ifdef CONFIG_SYS_BOOT_NORSPL
+ALL-y += $(obj)u-boot.ltq.norspl
+ALL-$(CONFIG_SPL_LZO_SUPPORT) += $(obj)u-boot.ltq.lzo.norspl
+ALL-$(CONFIG_SPL_LZMA_SUPPORT) += $(obj)u-boot.ltq.lzma.norspl
+endif
+endif
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/dcdc.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/soc.h>
+#include <asm/lantiq/io.h>
+
+#define LTQ_DCDC_CLK_SET0_CLK_SEL_P		(1 << 6)
+#define LTQ_DCDC_CLK_SET1_SEL_DIV25		(1 << 5)
+#define LTQ_DCDC_CONF_TEST_DIG_PID_FREEZE	(1 << 5)
+
+struct ltq_dcdc_regs {
+	u8	b0_coeh;		/* Coefficient b0 */
+	u8	b0_coel;		/* Coefficient b0 */
+	u8	b1_coeh;		/* Coefficient b1 */
+	u8	b1_coel;		/* Coefficient b1 */
+	u8	b2_coeh;		/* Coefficient b2 */
+	u8	b2_coel;		/* Coefficient b2 */
+	u8	clk_set0;		/* Clock setup */
+	u8	clk_set1;		/* Clock setup */
+	u8	pwm_confh;		/* Configure PWM */
+	u8	pwm_confl;		/* Configure PWM */
+	u8	bias_vreg0;		/* Bias and regulator setup */
+	u8	bias_vreg1;		/* Bias and regulator setup */
+	u8	adc_gen0;		/* ADC and general control */
+	u8	adc_gen1;		/* ADC and general control */
+	u8	adc_con0;		/* ADC and general config */
+	u8	adc_con1;		/* ADC and general config */
+	u8	conf_test_ana;		/* not documented */
+	u8	conf_test_dig;		/* not documented */
+	u8	dcdc_status;		/* not documented */
+	u8	pid_status;		/* not documented */
+	u8	duty_cycle;		/* not documented */
+	u8	non_ov_delay;		/* not documented */
+	u8	analog_gain;		/* not documented */
+	u8	duty_cycle_max_sat;	/* not documented */
+	u8	duty_cycle_min_sat;	/* not documented */
+	u8	duty_cycle_max;		/* not documented */
+	u8	duty_cycle_min;		/* not documented */
+	u8	error_max;		/* not documented */
+	u8	error_read;		/* not documented */
+	u8	delay_deglitch;		/* not documented */
+	u8	latch_control;		/* not documented */
+	u8	rsvd[240];
+	u8	osc_conf;		/* OSC general config */
+	u8	osc_stat;		/* OSC general status */
+};
+
+static struct ltq_dcdc_regs *ltq_dcdc_regs =
+	(struct ltq_dcdc_regs *) CKSEG1ADDR(LTQ_DCDC_BASE);
+
+void ltq_dcdc_init(unsigned int dig_ref)
+{
+	u8 dig_ref_cur, val;
+
+	/* Set duty cycle max sat. to 70/90, enable PID freeze */
+	ltq_writeb(&ltq_dcdc_regs->duty_cycle_max_sat, 0x5A);
+	ltq_writeb(&ltq_dcdc_regs->duty_cycle_min_sat, 0x46);
+	val = ltq_readb(&ltq_dcdc_regs->conf_test_dig);
+	val |= LTQ_DCDC_CONF_TEST_DIG_PID_FREEZE;
+	ltq_writeb(&ltq_dcdc_regs->conf_test_dig, val);
+
+	/* Program new coefficients */
+	ltq_writeb(&ltq_dcdc_regs->b0_coeh, 0x00);
+	ltq_writeb(&ltq_dcdc_regs->b0_coel, 0x00);
+	ltq_writeb(&ltq_dcdc_regs->b1_coeh, 0xFF);
+	ltq_writeb(&ltq_dcdc_regs->b1_coel, 0xE6);
+	ltq_writeb(&ltq_dcdc_regs->b2_coeh, 0x00);
+	ltq_writeb(&ltq_dcdc_regs->b2_coel, 0x1B);
+	ltq_writeb(&ltq_dcdc_regs->non_ov_delay, 0x8B);
+
+	/* Set duty cycle max sat. to 60/108, disable PID freeze */
+	ltq_writeb(&ltq_dcdc_regs->duty_cycle_max_sat, 0x6C);
+	ltq_writeb(&ltq_dcdc_regs->duty_cycle_min_sat, 0x3C);
+	val = ltq_readb(&ltq_dcdc_regs->conf_test_dig);
+	val &= ~LTQ_DCDC_CONF_TEST_DIG_PID_FREEZE;
+	ltq_writeb(&ltq_dcdc_regs->conf_test_dig, val);
+
+	/* Init clock and DLL settings */
+	val = ltq_readb(&ltq_dcdc_regs->clk_set0);
+	val |= LTQ_DCDC_CLK_SET0_CLK_SEL_P;
+	ltq_writeb(&ltq_dcdc_regs->clk_set0, val);
+	val = ltq_readb(&ltq_dcdc_regs->clk_set1);
+	val |= LTQ_DCDC_CLK_SET1_SEL_DIV25;
+	ltq_writeb(&ltq_dcdc_regs->clk_set1, val);
+	ltq_writeb(&ltq_dcdc_regs->pwm_confh, 0xF9);
+
+	wmb();
+
+	/* Adapt value of digital reference of DCDC converter */
+	dig_ref_cur = ltq_readb(&ltq_dcdc_regs->bias_vreg1);
+
+	while (dig_ref_cur != dig_ref) {
+		if (dig_ref >= dig_ref_cur)
+			dig_ref_cur++;
+		else if (dig_ref < dig_ref_cur)
+			dig_ref_cur--;
+
+		ltq_writeb(&ltq_dcdc_regs->bias_vreg1, dig_ref_cur);
+		__udelay(1000);
+	}
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/ebu.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/soc.h>
+#include <asm/lantiq/io.h>
+
+#define EBU_ADDRSEL_MASK(mask)		((mask & 0xf) << 4)
+#define EBU_ADDRSEL_REGEN		(1 << 0)
+
+#define EBU_CON_WRDIS			(1 << 31)
+#define EBU_CON_AGEN_DEMUX		(0x0 << 24)
+#define EBU_CON_AGEN_MUX		(0x2 << 24)
+#define EBU_CON_SETUP			(1 << 22)
+#define EBU_CON_WAIT_DIS		(0x0 << 20)
+#define EBU_CON_WAIT_ASYNC		(0x1 << 20)
+#define EBU_CON_WAIT_SYNC		(0x2 << 20)
+#define EBU_CON_WINV			(1 << 19)
+#define EBU_CON_PW_8BIT			(0x0 << 16)
+#define EBU_CON_PW_16BIT		(0x1 << 16)
+#define EBU_CON_ALEC(cycles)		((cycles & 0x3) << 14)
+#define EBU_CON_BCGEN_CS		(0x0 << 12)
+#define EBU_CON_BCGEN_INTEL		(0x1 << 12)
+#define EBU_CON_BCGEN_MOTOROLA		(0x2 << 12)
+#define EBU_CON_WAITWRC(cycles)		((cycles & 0x7) << 8)
+#define EBU_CON_WAITRDC(cycles)		((cycles & 0x3) << 6)
+#define EBU_CON_HOLDC(cycles)		((cycles & 0x3) << 4)
+#define EBU_CON_RECOVC(cycles)		((cycles & 0x3) << 2)
+#define EBU_CON_CMULT_1			0x0
+#define EBU_CON_CMULT_4			0x1
+#define EBU_CON_CMULT_8			0x2
+#define EBU_CON_CMULT_16		0x3
+
+#if defined(CONFIG_LTQ_SUPPORT_NOR_FLASH)
+#define ebu_region0_enable		1
+#else
+#define ebu_region0_enable		0
+#endif
+
+#if ((CONFIG_SYS_MAX_FLASH_BANKS == 2) && defined(CONFIG_LTQ_SUPPORT_NOR_FLASH) )
+#define ebu_region0_addrsel_mask	3
+#else
+#define ebu_region0_addrsel_mask	1
+#endif
+
+#if defined(CONFIG_LTQ_SUPPORT_NAND_FLASH) || ((CONFIG_SYS_MAX_FLASH_BANKS == 2) && defined(CONFIG_LTQ_SUPPORT_NOR_FLASH) )
+#define ebu_region1_enable		1
+#else
+#define ebu_region1_enable		0
+#endif
+
+struct ltq_ebu_regs {
+	u32	clc;
+	u32	rsvd0;
+	u32	id;
+	u32	rsvd1;
+	u32	con;
+	u32	rsvd2[3];
+	u32	addr_sel_0;
+	u32	addr_sel_1;
+	u32	addr_sel_2;
+	u32	addr_sel_3;
+	u32	rsvd3[12];
+	u32	con_0;
+	u32	con_1;
+	u32	con_2;
+	u32	con_3;
+};
+
+static struct ltq_ebu_regs *ltq_ebu_regs =
+	(struct ltq_ebu_regs *) CKSEG1ADDR(LTQ_EBU_BASE);
+
+void ltq_ebu_init(void)
+{
+	if (ebu_region0_enable) {
+		/*
+		 * Map EBU region 0 to range 0x10000000-0x13ffffff and enable
+		 * region control. This supports up to 32 MiB NOR flash in
+		 * bank 0.
+		 */
+		ltq_writel(&ltq_ebu_regs->addr_sel_0, LTQ_EBU_REGION0_BASE |
+			EBU_ADDRSEL_MASK(ebu_region0_addrsel_mask) | EBU_ADDRSEL_REGEN);
+
+		ltq_writel(&ltq_ebu_regs->con_0, EBU_CON_AGEN_DEMUX |
+			EBU_CON_WAIT_DIS | EBU_CON_PW_16BIT |
+			EBU_CON_ALEC(3) | EBU_CON_BCGEN_INTEL |
+			EBU_CON_WAITWRC(7) | EBU_CON_WAITRDC(3) |
+			EBU_CON_HOLDC(3) | EBU_CON_RECOVC(3) |
+			EBU_CON_CMULT_16);
+	} else
+		ltq_clrbits(&ltq_ebu_regs->addr_sel_0, EBU_ADDRSEL_REGEN);
+
+	if (ebu_region1_enable) {
+		/*
+		 * Map EBU region 1 to range 0x14000000-0x13ffffff and enable
+		 * region control. This supports NAND flash in bank 1. (and  NOR flash in bank 2)
+		 */
+		ltq_writel(&ltq_ebu_regs->addr_sel_1, LTQ_EBU_REGION1_BASE |
+			EBU_ADDRSEL_MASK(3) | EBU_ADDRSEL_REGEN);
+
+		if (ebu_region0_addrsel_mask == 1)
+			ltq_writel(&ltq_ebu_regs->con_1, EBU_CON_AGEN_DEMUX |
+				EBU_CON_SETUP | EBU_CON_WAIT_DIS | EBU_CON_PW_8BIT |
+				EBU_CON_ALEC(3) | EBU_CON_BCGEN_INTEL |
+				EBU_CON_WAITWRC(2) | EBU_CON_WAITRDC(2) |
+				EBU_CON_HOLDC(1) | EBU_CON_RECOVC(1) |
+				EBU_CON_CMULT_4);
+
+		if (ebu_region0_addrsel_mask == 3)
+			ltq_writel(&ltq_ebu_regs->con_1, EBU_CON_AGEN_DEMUX |
+				EBU_CON_WAIT_DIS | EBU_CON_PW_16BIT |
+				EBU_CON_ALEC(3) | EBU_CON_BCGEN_INTEL |
+				EBU_CON_WAITWRC(7) | EBU_CON_WAITRDC(3) |
+				EBU_CON_HOLDC(3) | EBU_CON_RECOVC(3) |
+				EBU_CON_CMULT_16);
+	} else
+		ltq_clrbits(&ltq_ebu_regs->addr_sel_1, EBU_ADDRSEL_REGEN);
+}
+
+void *flash_swap_addr(unsigned long addr)
+{
+	return (void *)(addr ^ 2);
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/gphy.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/lantiq/io.h>
+#include <asm/arch/soc.h>
+#include <asm/arch/gphy.h>
+#include <lzma/LzmaTypes.h>
+#include <lzma/LzmaDec.h>
+#include <lzma/LzmaTools.h>
+
+static inline void ltq_gphy_decompress(const void *fw_start, const void *fw_end,
+				ulong dst_addr)
+{
+	const ulong fw_len = (ulong) fw_end - (ulong) fw_start;
+	const ulong addr = CKSEG1ADDR(dst_addr);
+
+	debug("ltq_gphy_decompress: addr %08lx, fw_start %p, fw_end %p\n",
+		addr, fw_start, fw_end);
+
+	SizeT lzma_len = 65536;
+	int ret = lzmaBuffToBuffDecompress(
+	(unsigned char *)addr, &lzma_len,
+	(unsigned char *)fw_start, fw_len);
+}
+
+void ltq_gphy_phy11g_a1x_load(ulong addr)
+{
+	extern ulong __ltq_fw_phy11g_a1x_start;
+	extern ulong __ltq_fw_phy11g_a1x_end;
+
+	ltq_gphy_decompress(&__ltq_fw_phy11g_a1x_start,
+			    &__ltq_fw_phy11g_a1x_end,
+			    addr);
+}
+
+void ltq_gphy_phy11g_a2x_load(ulong addr)
+{
+	extern ulong __ltq_fw_phy11g_a2x_start;
+	extern ulong __ltq_fw_phy11g_a2x_end;
+
+	ltq_gphy_decompress(&__ltq_fw_phy11g_a2x_start,
+			    &__ltq_fw_phy11g_a2x_end,
+			    addr);
+}
+
+void ltq_gphy_phy22f_a1x_load(ulong addr)
+{
+	extern ulong __ltq_fw_phy22f_a1x_start;
+	extern ulong __ltq_fw_phy22f_a1x_end;
+
+	ltq_gphy_decompress(&__ltq_fw_phy22f_a1x_start,
+			    &__ltq_fw_phy22f_a1x_end,
+			    addr);
+}
+
+void ltq_gphy_phy22f_a2x_load(ulong addr)
+{
+	extern ulong __ltq_fw_phy22f_a2x_start;
+	extern ulong __ltq_fw_phy22f_a2x_end;
+
+	ltq_gphy_decompress(&__ltq_fw_phy22f_a2x_start,
+			    &__ltq_fw_phy22f_a2x_end,
+			    addr);
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/gphy_fw.S
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <asm/asm.h>
+
+	.section .rodata.__ltq_fw_phy11g_a1x
+EXPORT(__ltq_fw_phy11g_a1x_start)
+	.incbin "fw_phy11g_a1x.blob"
+EXPORT(__ltq_fw_phy11g_a1x_end)
+
+	.section .rodata.__ltq_fw_phy11g_a2x
+EXPORT(__ltq_fw_phy11g_a2x_start)
+	.incbin "fw_phy11g_a2x.blob"
+EXPORT(__ltq_fw_phy11g_a2x_end)
+
+	.section .rodata.__ltq_fw_phy22f_a1x
+EXPORT(__ltq_fw_phy22f_a1x_start)
+	.incbin "fw_phy22f_a1x.blob"
+EXPORT(__ltq_fw_phy22f_a1x_end)
+
+	.section .rodata.__ltq_fw_phy22f_a2x
+EXPORT(__ltq_fw_phy22f_a2x_start)
+	.incbin "fw_phy22f_a2x.blob"
+EXPORT(__ltq_fw_phy22f_a2x_end)
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/mem.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/soc.h>
+#include <asm/lantiq/io.h>
+
+#define LTQ_CCR03_EIGHT_BANK_MODE	(1 << 0)
+#define LTQ_CCR08_CS_MAP_SHIFT		24
+#define LTQ_CCR08_CS_MAP_MASK		(0x3 << LTQ_CCR08_CS_MAP_SHIFT)
+#define LTQ_CCR11_COLUMN_SIZE_SHIFT	24
+#define LTQ_CCR11_COLUMN_SIZE_MASK	(0x7 << LTQ_CCR11_COLUMN_SIZE_SHIFT)
+#define LTQ_CCR11_ADDR_PINS_MASK	0x7
+#define LTQ_CCR15_MAX_COL_REG_SHIFT	24
+#define LTQ_CCR15_MAX_COL_REG_MASK	(0xF << LTQ_CCR15_MAX_COL_REG_SHIFT)
+#define LTQ_CCR16_MAX_ROW_REG_MASK	0xF
+
+static void *ltq_mc_ddr_base = (void *) CKSEG1ADDR(LTQ_MC_DDR_BASE);
+
+static inline u32 ltq_mc_ccr_read(u32 index)
+{
+	return ltq_readl(ltq_mc_ddr_base + LTQ_MC_DDR_CCR_OFFSET(index));
+}
+
+phys_size_t initdram(int board_type)
+{
+	u32 max_col_reg, max_row_reg, column_size, addr_pins;
+	u32 banks, cs_map;
+	phys_size_t size;
+
+	banks = (ltq_mc_ccr_read(3) & LTQ_CCR03_EIGHT_BANK_MODE) ? 8 : 4;
+
+	cs_map = (ltq_mc_ccr_read(8) & LTQ_CCR08_CS_MAP_MASK) >>
+		LTQ_CCR08_CS_MAP_SHIFT;
+
+	column_size = (ltq_mc_ccr_read(11) & LTQ_CCR11_COLUMN_SIZE_MASK) >>
+		LTQ_CCR11_COLUMN_SIZE_SHIFT;
+
+	addr_pins = ltq_mc_ccr_read(11) & LTQ_CCR11_ADDR_PINS_MASK;
+
+	max_col_reg = (ltq_mc_ccr_read(15) & LTQ_CCR15_MAX_COL_REG_MASK) >>
+		LTQ_CCR15_MAX_COL_REG_SHIFT;
+
+	max_row_reg = ltq_mc_ccr_read(16) & LTQ_CCR16_MAX_ROW_REG_MASK;
+
+	/*
+	 * size (bytes) = 2 ^ rowsize * 2 ^ colsize * banks * chipselects
+	 *                 * datawidth (bytes)
+	 */
+	size = (2 << (max_col_reg - column_size - 1)) *
+		(2 << (max_row_reg - addr_pins - 1)) * banks * cs_map * 2;
+
+	return size;
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/mem_init.S
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <config.h>
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/addrspace.h>
+#include <asm/arch/soc.h>
+
+/* Must be configured in BOARDDIR */
+#include <ddr_settings.h>
+
+#define LTQ_MC_DDR_START		(1 << 8)
+#define LTQ_MC_DDR_DLL_LOCK_IND	1
+
+#define CCS_ALWAYS_LAST			0x0430
+#define CCS_AHBM_CR_BURST_EN		(1 << 2)
+#define CCS_FPIM_CR_BURST_EN		(1 << 1)
+
+#define CCR03_EIGHT_BANK_MODE		(1 << 0)
+
+	/* Store given value in MC DDR CCRx register */
+	.macro ccr_sw num, val
+	li	t1, \val
+	sw	t1, LTQ_MC_DDR_CCR_OFFSET(\num)(t0)
+	.endm
+
+LEAF(ltq_mem_init)
+	/* Load MC DDR module base */
+	li	t0, (LTQ_MC_DDR_BASE | KSEG1)
+
+	/* Put memory controller in inactive mode */
+	sw	zero, LTQ_MC_DDR_CCR_OFFSET(7)(t0)
+
+	/* Init MC DDR CCR registers with values from ddr_settings.h */
+	ccr_sw	0, MC_CCR00_VALUE
+	ccr_sw	1, MC_CCR01_VALUE
+	ccr_sw	2, MC_CCR02_VALUE
+	ccr_sw	3, MC_CCR03_VALUE
+	ccr_sw	4, MC_CCR04_VALUE
+	ccr_sw	5, MC_CCR05_VALUE
+	ccr_sw	6, MC_CCR06_VALUE
+	ccr_sw	7, MC_CCR07_VALUE
+	ccr_sw	8, MC_CCR08_VALUE
+	ccr_sw	9, MC_CCR09_VALUE
+
+	ccr_sw	10, MC_CCR10_VALUE
+	ccr_sw	11, MC_CCR11_VALUE
+	ccr_sw	12, MC_CCR12_VALUE
+	ccr_sw	13, MC_CCR13_VALUE
+	ccr_sw	14, MC_CCR14_VALUE
+	ccr_sw	15, MC_CCR15_VALUE
+	ccr_sw	16, MC_CCR16_VALUE
+	ccr_sw	17, MC_CCR17_VALUE
+	ccr_sw	18, MC_CCR18_VALUE
+	ccr_sw	19, MC_CCR19_VALUE
+
+	ccr_sw	20, MC_CCR20_VALUE
+	ccr_sw	21, MC_CCR21_VALUE
+	ccr_sw	22, MC_CCR22_VALUE
+	ccr_sw	23, MC_CCR23_VALUE
+	ccr_sw	24, MC_CCR24_VALUE
+	ccr_sw	25, MC_CCR25_VALUE
+	ccr_sw	26, MC_CCR26_VALUE
+	ccr_sw	27, MC_CCR27_VALUE
+	ccr_sw	28, MC_CCR28_VALUE
+	ccr_sw	29, MC_CCR29_VALUE
+
+	ccr_sw	30, MC_CCR30_VALUE
+	ccr_sw	31, MC_CCR31_VALUE
+	ccr_sw	32, MC_CCR32_VALUE
+	ccr_sw	33, MC_CCR33_VALUE
+	ccr_sw	34, MC_CCR34_VALUE
+	ccr_sw	35, MC_CCR35_VALUE
+	ccr_sw	36, MC_CCR36_VALUE
+	ccr_sw	37, MC_CCR37_VALUE
+	ccr_sw	38, MC_CCR38_VALUE
+	ccr_sw	39, MC_CCR39_VALUE
+
+	ccr_sw	40, MC_CCR40_VALUE
+	ccr_sw	41, MC_CCR41_VALUE
+	ccr_sw	42, MC_CCR42_VALUE
+	ccr_sw	43, MC_CCR43_VALUE
+	ccr_sw	44, MC_CCR44_VALUE
+	ccr_sw	45, MC_CCR45_VALUE
+	ccr_sw	46, MC_CCR46_VALUE
+
+	ccr_sw	52, MC_CCR52_VALUE
+	ccr_sw	53, MC_CCR53_VALUE
+	ccr_sw	54, MC_CCR54_VALUE
+	ccr_sw	55, MC_CCR55_VALUE
+	ccr_sw	56, MC_CCR56_VALUE
+	ccr_sw	57, MC_CCR57_VALUE
+	ccr_sw	58, MC_CCR58_VALUE
+	ccr_sw	59, MC_CCR59_VALUE
+
+	ccr_sw	60, MC_CCR60_VALUE
+	ccr_sw	61, MC_CCR61_VALUE
+
+	/* Disable bursts between FPI Master bus and XBAR bus */
+	li	t4, (LTQ_MC_GLOBAL_BASE | KSEG1)
+	li	t5, CCS_AHBM_CR_BURST_EN
+	sw	t5, CCS_ALWAYS_LAST(t4)
+
+	/* Init abort condition for DRAM probe */
+	move	t4, zero
+
+	/*
+	 * Put memory controller in active mode and start initialitation
+	 * sequence for connected DDR-SDRAM device
+	 */
+mc_start:
+	lw	t1, LTQ_MC_DDR_CCR_OFFSET(7)(t0)
+	li	t2, LTQ_MC_DDR_START
+	or	t1, t1, t2
+	sw	t1, LTQ_MC_DDR_CCR_OFFSET(7)(t0)
+
+	/*
+	 * Wait until DLL has locked and core is ready for data transfers.
+	 * DLL lock indication is in register CCR47 and CCR48
+	 */
+wait_ready:
+	li	t1, LTQ_MC_DDR_DLL_LOCK_IND
+	lw	t2, LTQ_MC_DDR_CCR_OFFSET(47)(t0)
+	and	t2, t2, t1
+	bne	t1, t2, wait_ready
+
+	lw	t2, LTQ_MC_DDR_CCR_OFFSET(48)(t0)
+	and	t2, t2, t1
+	bne	t1, t2, wait_ready
+
+#ifdef CONFIG_SYS_DRAM_PROBE
+dram_probe:
+	/* Initialization is finished after the second MC start */
+	bnez	t4, mc_finished
+
+	/*
+	 * Preload register values for CCR03 and CCR11. Initial settings
+	 * are 8-bank mode enabled, 14 use address row bits, 10 used
+	 * column address bits.
+	 */
+	li	t1, CONFIG_SYS_SDRAM_BASE_UC
+	li	t5, MC_CCR03_VALUE
+	li	t6, MC_CCR11_VALUE
+	addi	t4, t4, 1
+
+	/*
+	 * Store test values to DRAM at offsets 0 and 2^13 (bit 2 in bank select
+	 * address BA[3]) and read back the value at offset 0. If the resulting
+	 * value is equal to 1 we can skip to the next test. Otherwise
+	 * the 8-bank mode does not work with the current DRAM device,
+	 * thus we need to clear the according bit in register CCR03.
+	 */
+	li	t2, 1
+	sw	t2, 0x0(t1)
+	li	t3, (1 << 13)
+	add	t3, t3, t1
+	sw	zero, 0(t3)
+	lw	t3, 0(t1)
+	bnez	t3, row_col_test
+
+	/* Clear CCR03.EIGHT_BANK_MODE */
+	li	t3, ~CCR03_EIGHT_BANK_MODE
+	and	t5, t5, t3
+
+row_col_test:
+	/*
+	 * Store test values to DRAM at offsets 0, 2^27 (bit 13 of row address
+	 * RA[14]) and 2^26 (bit 12 of RA[14]). The chosen test values
+	 * represent the difference between max. row address bits (14) and used
+	 * row address bits. Then the read back value at offset 0 indicates
+	 * the useable row address bits with the current DRAM device. This
+	 * value must be set in the CCR11 register.
+	 */
+	sw	zero, 0(t1)
+
+	li	t2, 1
+	li	t3, (1 << 27)
+	add	t3, t3, t1
+	sw	t2, 0(t3)
+
+	li	t2, 2
+	li	t3, (1 << 26)
+	add	t3, t3, t1
+	sw	t2, 0(t3)
+
+	/* Update CCR11.ADDR_PINS */
+	lw	t3, 0(t1)
+	add	t6, t6, t3
+
+	/*
+	 * Store test values to DRAM at offsets 0, 2^10 (bit 9 of column address
+	 * CA[10]) and 2^9 (bit 8 of CA[10]). The chosen test values represent
+	 * the difference between max. column address bits (12) and used
+	 * column address bits. Then the read back value at offset 0 indicates
+	 * the useable column address bits with the current DRAM device. This
+	 * value must be set in the CCR11 register.
+	 */
+	sw	zero, 0(t1)
+
+	li	t2, 1
+	li	t3, (1 << 10)
+	add	t3, t3, t1
+	sw	t2, 0(t3)
+
+	li	t2, 2
+	li	t3, (1 << 9)
+	add	t3, t3, t1
+	sw	t2, 0(t3)
+
+	/* Update CCR11.COLUMN_SIZE */
+	lw	t3, 0(t1)
+	sll	t3, t3, 24
+	add	t6, t6, t3
+
+	/* Put memory controller in inactive mode */
+	sw	zero, LTQ_MC_DDR_CCR_OFFSET(7)(t0)
+
+	/* Update CCR03 and CCR11 and restart memory controller initialiation */
+	sw	t5, LTQ_MC_DDR_CCR_OFFSET(3)(t0)
+	sw	t6, LTQ_MC_DDR_CCR_OFFSET(11)(t0)
+	b	mc_start
+
+mc_finished:
+#endif /* CONFIG_SYS_DRAM_PROBE */
+
+	jr	ra
+
+	END(ltq_mem_init)
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/pmu.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/lantiq/io.h>
+#include <asm/lantiq/pm.h>
+#include <asm/arch/soc.h>
+
+#define LTQ_PMU_PWDCR_RESERVED		((1 << 13) | (1 << 4))
+
+#define LTQ_PMU_PWDCR_PCIELOC_EN	(1 << 31)
+#define LTQ_PMU_PWDCR_GPHY		(1 << 30)
+#define LTQ_PMU_PWDCR_PPE_TOP		(1 << 29)
+#define LTQ_PMU_PWDCR_SWITCH		(1 << 28)
+#define LTQ_PMU_PWDCR_USB1		(1 << 27)
+#define LTQ_PMU_PWDCR_USB1_PHY		(1 << 26)
+#define LTQ_PMU_PWDCR_TDM		(1 << 25)
+#define LTQ_PMU_PWDCR_PPE_DPLUS		(1 << 24)
+#define LTQ_PMU_PWDCR_PPE_DPLUM		(1 << 23)
+#define LTQ_PMU_PWDCR_PPE_EMA		(1 << 22)
+#define LTQ_PMU_PWDCR_PPE_TC		(1 << 21)
+#define LTQ_PMU_PWDCR_DEU		(1 << 20)
+#define LTQ_PMU_PWDCR_PPE_SLL01		(1 << 19)
+#define LTQ_PMU_PWDCR_PPE_QSB		(1 << 18)
+#define LTQ_PMU_PWDCR_UART1		(1 << 17)
+#define LTQ_PMU_PWDCR_SDIO		(1 << 16)
+#define LTQ_PMU_PWDCR_AHBM		(1 << 15)
+#define LTQ_PMU_PWDCR_FPIM		(1 << 14)
+#define LTQ_PMU_PWDCR_GPTC		(1 << 12)
+#define LTQ_PMU_PWDCR_LEDC		(1 << 11)
+#define LTQ_PMU_PWDCR_EBU		(1 << 10)
+#define LTQ_PMU_PWDCR_DSL		(1 << 9)
+#define LTQ_PMU_PWDCR_SPI		(1 << 8)
+#define LTQ_PMU_PWDCR_USIF		(1 << 7)
+#define LTQ_PMU_PWDCR_USB0		(1 << 6)
+#define LTQ_PMU_PWDCR_DMA		(1 << 5)
+#define LTQ_PMU_PWDCR_DFEV1		(1 << 3)
+#define LTQ_PMU_PWDCR_DFEV0		(1 << 2)
+#define LTQ_PMU_PWDCR_FPIS		(1 << 1)
+#define LTQ_PMU_PWDCR_USB0_PHY		(1 << 0)
+
+struct ltq_pmu_regs {
+	u32	rsvd0[7];
+	u32	pwdcr;		/* Power down control */
+	u32	sr;		/* Power down status */
+	u32	pwdcr1;		/* Power down control 1 */
+	u32	sr1;		/* Power down status 1 */
+};
+
+static struct ltq_pmu_regs *ltq_pmu_regs =
+	(struct ltq_pmu_regs *) CKSEG1ADDR(LTQ_PMU_BASE);
+
+u32 ltq_pm_map(enum ltq_pm_modules module)
+{
+	u32 val;
+
+	switch (module) {
+	case LTQ_PM_CORE:
+		val = LTQ_PMU_PWDCR_UART1 | LTQ_PMU_PWDCR_FPIM |
+			LTQ_PMU_PWDCR_LEDC | LTQ_PMU_PWDCR_EBU;
+		break;
+	case LTQ_PM_DMA:
+		val = LTQ_PMU_PWDCR_DMA;
+		break;
+	case LTQ_PM_ETH:
+		val = LTQ_PMU_PWDCR_GPHY | LTQ_PMU_PWDCR_PPE_TOP |
+			LTQ_PMU_PWDCR_SWITCH | LTQ_PMU_PWDCR_PPE_DPLUS |
+			LTQ_PMU_PWDCR_PPE_DPLUM | LTQ_PMU_PWDCR_PPE_EMA |
+			LTQ_PMU_PWDCR_PPE_TC | LTQ_PMU_PWDCR_PPE_SLL01 |
+			LTQ_PMU_PWDCR_PPE_QSB;
+		break;
+	case LTQ_PM_SPI:
+		val = LTQ_PMU_PWDCR_SPI;
+		break;
+	default:
+		val = 0;
+		break;
+	}
+
+	return val;
+}
+
+int ltq_pm_enable(enum ltq_pm_modules module)
+{
+	const unsigned long timeout = 1000;
+	unsigned long timebase;
+	u32 sr, val;
+
+	val = ltq_pm_map(module);
+	if (unlikely(!val))
+		return 1;
+
+	ltq_clrbits(&ltq_pmu_regs->pwdcr, val);
+
+	timebase = get_timer(0);
+
+	do {
+		sr = ltq_readl(&ltq_pmu_regs->sr);
+		if (~sr & val)
+			return 0;
+	} while (get_timer(timebase) < timeout);
+
+	return 1;
+}
+
+int ltq_pm_disable(enum ltq_pm_modules module)
+{
+	u32 val;
+
+	val = ltq_pm_map(module);
+	if (unlikely(!val))
+		return 1;
+
+	ltq_setbits(&ltq_pmu_regs->pwdcr, val);
+
+	return 0;
+}
+
+void ltq_pmu_init(void)
+{
+	u32 set, clr;
+
+	clr = ltq_pm_map(LTQ_PM_CORE);
+	set = ~(LTQ_PMU_PWDCR_RESERVED | clr);
+
+	ltq_clrsetbits(&ltq_pmu_regs->pwdcr, clr, set);
+}
--- /dev/null
+++ b/arch/mips/cpu/mips32/vrx200/rcu.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/lantiq/io.h>
+#include <asm/lantiq/reset.h>
+#include <asm/lantiq/cpu.h>
+#include <asm/arch/soc.h>
+
+#define LTQ_RCU_RD_GPHY0	(1 << 31)	/* GPHY0 */
+#define LTQ_RCU_RD_SRST		(1 << 30)	/* Global SW Reset */
+#define LTQ_RCU_RD_GPHY1	(1 << 29)	/* GPHY1 */
+#define LTQ_RCU_RD_ENMIP2	(1 << 28)	/* Enable NMI of PLL2 */
+#define LTQ_RCU_RD_REG25_PD	(1 << 26)	/* Power down 2.5V regulator */
+#define LTQ_RCU_RD_ENDINIT	(1 << 25)	/* FPI slave bus access */
+#define LTQ_RCU_RD_PPE_ATM_TC	(1 << 23)	/* PPE ATM TC */
+#define LTQ_RCU_RD_PCIE		(1 << 22)	/* PCI-E core */
+#define LTQ_RCU_RD_ETHSW	(1 << 21)	/* Ethernet switch */
+#define LTQ_RCU_RD_DSP_DEN	(1 << 20)	/* Enable DSP JTAG */
+#define LTQ_RCU_RD_TDM		(1 << 19)	/* TDM module interface */
+#define LTQ_RCU_RD_ENMIP1	(1 << 18)	/* Enable NMI of PLL1 */
+#define LTQ_RCU_RD_SWBCK	(1 << 17)	/* Switch backward compat */
+#define LTQ_RCU_RD_HSNAND	(1 << 16)	/* HSNAND controller */
+#define LTQ_RCU_RD_ENMIP0	(1 << 15)	/* Enable NMI of PLL0 */
+#define LTQ_RCU_RD_MC		(1 << 14)	/* Memory Controller */
+#define LTQ_RCU_RD_PCI		(1 << 13)	/* PCI core */
+#define LTQ_RCU_RD_PCIE_PHY	(1 << 12)	/* PCI-E Phy */
+#define LTQ_RCU_RD_DFE_CORE	(1 << 11)	/* DFE core */
+#define LTQ_RCU_RD_SDIO		(1 << 10)	/* SDIO core */
+#define LTQ_RCU_RD_DMA		(1 << 9)	/* DMA core */
+#define LTQ_RCU_RD_PPE		(1 << 8)	/* PPE core */
+#define LTQ_RCU_RD_DFE		(1 << 7)	/* DFE core */
+#define LTQ_RCU_RD_AHB		(1 << 6)	/* AHB bus */
+#define LTQ_RCU_RD_HRST_CFG	(1 << 5)	/* HW reset configuration */
+#define LTQ_RCU_RD_USB		(1 << 4)	/* USB and Phy core */
+#define LTQ_RCU_RD_PPE_DSP	(1 << 3)	/* PPE DSP interface */
+#define LTQ_RCU_RD_FPI		(1 << 2)	/* FPI bus */
+#define LTQ_RCU_RD_CPU		(1 << 1)	/* CPU subsystem */
+#define LTQ_RCU_RD_HRST		(1 << 0)	/* HW reset via HRST pin */
+
+#define LTQ_RCU_STAT_BOOT_SHIFT		17
+#define LTQ_RCU_STAT_BOOT_MASK		(0xF << LTQ_RCU_STAT_BOOT_SHIFT)
+#define LTQ_RCU_STAT_BOOT_H		(1 << 12)
+
+#define LTQ_RCU_GP_STRAP_CLOCKSOURCE	(1 << 15)
+
+struct ltq_rcu_regs {
+	u32	rsvd0[4];
+	u32	req;		/* Reset request */
+	u32	stat;		/* Reset status */
+	u32	usb0_cfg;	/* USB0 configure */
+	u32	gp_strap;	/* GPIO strapping */
+	u32	gfs_add0;	/* GPHY0 firmware base addr */
+	u32	stat2;		/* SLIC and USB reset status */
+	u32	pci_rdy;	/* PCI boot ready */
+	u32	ppe_conf;	/* PPE ethernet config */
+	u32	pcie_phy_con;	/* PCIE PHY config/status */
+	u32	usb1_cfg;	/* USB1 configure */
+	u32	usb_ana_cfg1a;	/* USB analog config 1a */
+	u32	usb_ana_cfg1b;	/* USB analog config 1b */
+	u32	rsvd1;
+	u32	gf_mdio_add;	/* GPHY0/1 MDIO address */
+	u32	req2;		/* SLIC and USB reset request */
+	u32	ahb_endian;	/* AHB bus endianess */
+	u32	rsvd2[4];
+	u32	gcc;		/* General CPU config */
+	u32	rsvd3;
+	u32	gfs_add1;	/* GPHY1 firmware base addr */
+};
+
+static struct ltq_rcu_regs *ltq_rcu_regs =
+	(struct ltq_rcu_regs *) CKSEG1ADDR(LTQ_RCU_BASE);
+
+u32 ltq_reset_map(enum ltq_reset_modules module)
+{
+	u32 val;
+
+	switch (module) {
+	case LTQ_RESET_CORE:
+	case LTQ_RESET_SOFT:
+		val = LTQ_RCU_RD_SRST | LTQ_RCU_RD_CPU | LTQ_RCU_RD_ENMIP2 |
+			LTQ_RCU_RD_GPHY1 | LTQ_RCU_RD_GPHY0;
+		break;
+	case LTQ_RESET_DMA:
+		val = LTQ_RCU_RD_DMA;
+		break;
+	case LTQ_RESET_ETH:
+		val = LTQ_RCU_RD_PPE | LTQ_RCU_RD_ETHSW;
+		break;
+	case LTQ_RESET_PHY:
+		val = LTQ_RCU_RD_GPHY1 | LTQ_RCU_RD_GPHY0;
+		break;
+	case LTQ_RESET_HARD:
+		val = LTQ_RCU_RD_HRST;
+		break;
+	default:
+		val = 0;
+		break;
+	}
+
+	return val;
+}
+
+int ltq_reset_activate(enum ltq_reset_modules module)
+{
+	u32 val;
+
+	val = ltq_reset_map(module);
+	if (unlikely(!val))
+		return 1;
+
+	ltq_setbits(&ltq_rcu_regs->req, val);
+
+	return 0;
+}
+
+int ltq_reset_deactivate(enum ltq_reset_modules module)
+{
+	u32 val;
+
+	val = ltq_reset_map(module);
+	if (unlikely(!val))
+		return 1;
+
+	ltq_clrbits(&ltq_rcu_regs->req, val);
+
+	return 0;
+}
+
+enum ltq_boot_select ltq_boot_select(void)
+{
+	u32 stat;
+	unsigned int bootstrap;
+
+	/*
+	 * Boot select value is built from bits 20-17 and bit 12.
+	 * The bit sequence is read as 4-2-1-0-3.
+	 */
+	stat = ltq_readl(&ltq_rcu_regs->stat);
+	bootstrap = ((stat & LTQ_RCU_STAT_BOOT_H) << 4) |
+		((stat & LTQ_RCU_STAT_BOOT_MASK) >> LTQ_RCU_STAT_BOOT_SHIFT);
+
+	switch (bootstrap) {
+	case 0:
+		return BOOT_NOR_NO_BOOTROM;
+	case 1:
+		return BOOT_RGMII1;
+	case 2:
+		return BOOT_NOR;
+	case 4:
+		return BOOT_UART_NO_EEPROM;
+	case 6:
+		return BOOT_PCI;
+	case 8:
+		return BOOT_UART;
+	case 10:
+		return BOOT_SPI;
+	case 12:
+		return BOOT_NAND;
+	default:
+		return BOOT_UNKNOWN;
+	}
+}
+
+void ltq_rcu_gphy_boot(unsigned int id, ulong addr)
+{
+	u32 module;
+	void *gfs_add;
+
+	switch (id) {
+	case 0:
+		module = LTQ_RCU_RD_GPHY0;
+		gfs_add = &ltq_rcu_regs->gfs_add0;
+		break;
+	case 1:
+		module = LTQ_RCU_RD_GPHY1;
+		gfs_add = &ltq_rcu_regs->gfs_add1;
+		break;
+	default:
+		BUG();
+	}
+
+	/* Stop and reset GPHY */
+	ltq_setbits(&ltq_rcu_regs->req, module);
+
+	/* Configure firmware and boot address */
+	ltq_writel(gfs_add, CPHYSADDR(addr & 0xFFFFC000));
+
+	/* Start GPHY by releasing reset */
+	ltq_clrbits(&ltq_rcu_regs->req, module);
+}
--- /dev/null
+++ b/arch/mips/include/asm/arch-danube/config.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007-2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ * Common board configuration for Lantiq XWAY Danube family
+ *
+ * Use following defines in your board config to enable specific features
+ * and drivers for this SoC:
+ *
+ * CONFIG_LTQ_SUPPORT_UART
+ * - support the Danube ASC/UART interface and console
+ *
+ * CONFIG_LTQ_SUPPORT_NOR_FLASH
+ * - support a parallel NOR flash via the CFI interface in flash bank 0
+ *
+ * CONFIG_LTQ_SUPPORT_ETHERNET
+ * - support the Danube ETOP and MAC interface
+ *
+ * CONFIG_LTQ_SUPPORT_SPI_FLASH
+ * - support the Danube SPI interface and serial flash drivers
+ * - specific SPI flash drivers must be configured separately
+ */
+
+#ifndef __DANUBE_CONFIG_H__
+#define __DANUBE_CONFIG_H__
+
+/* CPU and SoC type */
+#define CONFIG_SOC_LANTIQ
+#define CONFIG_SOC_XWAY_DANUBE
+
+/* Cache configuration */
+#define CONFIG_SYS_MIPS_CACHE_MODE	CONF_CM_CACHABLE_NONCOHERENT
+#define CONFIG_SYS_DCACHE_SIZE		(16 * 1024)
+#define CONFIG_SYS_ICACHE_SIZE		(16 * 1024)
+#define CONFIG_SYS_CACHELINE_SIZE	32
+#define CONFIG_SYS_MIPS_CACHE_EXT_INIT
+
+/*
+ * Supported clock modes
+ * PLL0 clock output is 333 MHz
+ * PLL1 clock output is 262.144 MHz
+ */
+#define LTQ_CLK_CPU_333_DDR_167		0	/* Base PLL0, OCP 2 */
+#define LTQ_CLK_CPU_111_DDR_111		1	/* Base PLL0, OCP 1 */
+
+/* CPU speed */
+#define CONFIG_SYS_CLOCK_MODE		LTQ_CLK_CPU_333_DDR_167
+#define CONFIG_SYS_MIPS_TIMER_FREQ	166666667
+#define CONFIG_SYS_HZ			1000
+
+/* RAM */
+#define CONFIG_NR_DRAM_BANKS		1
+#define CONFIG_SYS_SDRAM_BASE		0x80000000
+#define CONFIG_SYS_MEMTEST_START	0x81000000
+#define CONFIG_SYS_MEMTEST_END		0x82000000
+#define CONFIG_SYS_LOAD_ADDR		0x81000000
+#define CONFIG_SYS_LOAD_SIZE		(2 * 1024 * 1024)
+#define CONFIG_SYS_INIT_SP_OFFSET	0x4000
+
+/* SRAM */
+#define CONFIG_SYS_SRAM_BASE		0xBE1A0000
+#define CONFIG_SYS_SRAM_SIZE		0x10000
+
+/* ASC/UART driver and console */
+#define CONFIG_LANTIQ_SERIAL
+#define CONFIG_SYS_BAUDRATE_TABLE	{ 9600, 19200, 38400, 57600, 115200 }
+
+/* GPIO */
+#define CONFIG_LANTIQ_GPIO
+#define CONFIG_LTQ_GPIO_MAX_BANKS	2
+
+/* FLASH driver */
+#if defined(CONFIG_LTQ_SUPPORT_NOR_FLASH)
+#define CONFIG_SYS_MAX_FLASH_BANKS	1
+#define CONFIG_SYS_MAX_FLASH_SECT	256
+#define CONFIG_SYS_FLASH_BASE		0xB0000000
+#define CONFIG_FLASH_16BIT
+#define CONFIG_SYS_FLASH_CFI
+#define CONFIG_FLASH_CFI_DRIVER
+#define CONFIG_SYS_FLASH_CFI_WIDTH	FLASH_CFI_16BIT
+#define CONFIG_SYS_FLASH_USE_BUFFER_WRITE
+#define CONFIG_FLASH_SHOW_PROGRESS	50
+#define CONFIG_SYS_FLASH_PROTECTION
+#define CONFIG_CFI_FLASH_USE_WEAK_ADDR_SWAP
+
+#define CONFIG_CMD_FLASH
+#else
+#define CONFIG_SYS_NO_FLASH
+#endif /* CONFIG_NOR_FLASH */
+
+#if defined(CONFIG_LTQ_SUPPORT_SPI_FLASH)
+#define CONFIG_LANTIQ_SPI
+#define CONFIG_SPI_FLASH
+
+#define CONFIG_CMD_SF
+#define CONFIG_CMD_SPI
+#endif
+
+#if defined(CONFIG_LTQ_SUPPORT_NAND_FLASH)
+#define CONFIG_NAND_LANTIQ
+#define CONFIG_SYS_MAX_NAND_DEVICE	1
+#define CONFIG_SYS_NAND_BASE		0xB4000000
+
+#define CONFIG_CMD_NAND
+#endif
+
+#if defined(CONFIG_LTQ_SUPPORT_ETHERNET)
+#define CONFIG_LANTIQ_DMA
+#define CONFIG_LANTIQ_DANUBE_ETOP
+
+#define CONFIG_PHYLIB
+#define CONFIG_MII
+
+#define CONFIG_CMD_MII
+#define CONFIG_CMD_NET
+#endif
+
+#define CONFIG_SPL_MAX_SIZE		(32 * 1024)
+#define CONFIG_SPL_BSS_MAX_SIZE		(8 * 1024)
+#define CONFIG_SPL_STACK_MAX_SIZE	(8 * 1024)
+#define CONFIG_SPL_MALLOC_MAX_SIZE	(32 * 1024)
+/*#define CONFIG_SPL_STACK_BSS_IN_SRAM*/
+
+#if defined(CONFIG_SPL_STACK_BSS_IN_SRAM)
+#define CONFIG_SPL_STACK_BASE		(CONFIG_SYS_SRAM_BASE + \
+					CONFIG_SPL_MAX_SIZE + \
+					CONFIG_SPL_STACK_MAX_SIZE - 1)
+#define CONFIG_SPL_BSS_BASE	  	(CONFIG_SPL_STACK_BASE + 1)
+#define CONFIG_SPL_MALLOC_BASE		(CONFIG_SYS_SDRAM_BASE + \
+					CONFIG_SYS_INIT_SP_OFFSET)
+#else
+#define CONFIG_SPL_STACK_BASE		(CONFIG_SYS_SDRAM_BASE + \
+					CONFIG_SYS_INIT_SP_OFFSET + \
+					CONFIG_SPL_STACK_MAX_SIZE - 1)
+#define CONFIG_SPL_BSS_BASE		(CONFIG_SPL_STACK_BASE + 1)
+#define CONFIG_SPL_MALLOC_BASE		(CONFIG_SPL_BSS_BASE + \
+					CONFIG_SPL_BSS_MAX_SIZE)
+#endif
+
+#if defined(CONFIG_SYS_BOOT_RAM)
+#define CONFIG_SYS_TEXT_BASE		0xa0100000
+#define CONFIG_SKIP_LOWLEVEL_INIT
+#define CONFIG_SYS_DISABLE_CACHE
+#endif
+
+#if defined(CONFIG_SYS_BOOT_NOR)
+#define CONFIG_SYS_TEXT_BASE		0xB0000000
+#endif
+
+#if defined(CONFIG_SYS_BOOT_NORSPL)
+#define CONFIG_SYS_TEXT_BASE		0x80100000
+#define CONFIG_SPL_TEXT_BASE		0xB0000000
+#endif
+
+#if defined(CONFIG_SYS_BOOT_NOR) || defined(CONFIG_SYS_BOOT_NORSPL)
+#define CONFIG_SYS_XWAY_EBU_BOOTCFG	0x688C688C
+#define CONFIG_XWAY_SWAP_BYTES
+#endif
+
+#define	CONFIG_SYS_MONITOR_BASE		CONFIG_SYS_TEXT_BASE
+
+#endif /* __DANUBE_CONFIG_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/arch-danube/gpio.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __DANUBE_GPIO_H__
+#define __DANUBE_GPIO_H__
+
+#include <asm/lantiq/gpio.h>
+
+#endif /* __DANUBE_GPIO_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/arch-danube/nand.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) 2012-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __DANUBE_NAND_H__
+#define __DANUBE_NAND_H__
+
+struct nand_chip;
+int ltq_nand_init(struct nand_chip *nand);
+
+#endif /* __DANUBE_NAND_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/arch-danube/soc.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007-2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __DANUBE_SOC_H__
+#define __DANUBE_SOC_H__
+
+#define LTQ_ASC0_BASE			0x1E100400
+#define LTQ_SPI_BASE			0x1E100800
+#define LTQ_GPIO_BASE			0x1E100B00
+#define LTQ_SSIO_BASE			0x1E100BB0
+#define LTQ_ASC1_BASE			0x1E100C00
+#define LTQ_DMA_BASE			0x1E104100
+
+#define LTQ_EBU_BASE			0x1E105300
+#define LTQ_EBU_REGION0_BASE		0x10000000
+#define LTQ_EBU_REGION1_BASE		0x14000000
+#define LTQ_EBU_NAND_BASE		(LTQ_EBU_BASE + 0xB0)
+
+#define LTQ_PPE_BASE			0x1E180000
+#define LTQ_PPE_ETOP_BASE		(LTQ_PPE_BASE + 0x11800)
+#define LTQ_PPE_ENET0_BASE		(LTQ_PPE_BASE + 0x11840)
+
+#define LTQ_PMU_BASE			0x1F102000
+#define LTQ_CGU_BASE			0x1F103000
+#define LTQ_MPS_BASE			0x1F107000
+#define LTQ_CHIPID_BASE			(LTQ_MPS_BASE + 0x340)
+#define LTQ_RCU_BASE			0x1F203000
+
+#define LTQ_MC_GEN_BASE			0x1F800000
+#define LTQ_MC_SDR_BASE			0x1F800200
+#define LTQ_MC_DDR_BASE			0x1F801000
+#define LTQ_MC_DDR_DC_OFFSET(x)		(x * 0x10)
+
+#endif /* __DANUBE_SOC_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/arch-vrx200/config.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ * Common board configuration for Lantiq XWAY VRX200 family
+ *
+ * Use following defines in your board config to enable specific features
+ * and drivers for this SoC:
+ *
+ * CONFIG_LTQ_SUPPORT_UART
+ * - support the VRX200 ASC/UART interface and console
+ *
+ * CONFIG_LTQ_SUPPORT_NOR_FLASH
+ * - support a parallel NOR flash via the CFI interface in flash bank 0
+ *
+ * CONFIG_LTQ_SUPPORT_ETHERNET
+ * - support the VRX200 internal switch
+ *
+ * CONFIG_LTQ_SUPPORT_SPI_FLASH
+ * - support the VRX200 SPI interface and serial flash drivers
+ * - specific SPI flash drivers must be configured separately
+ *
+ * CONFIG_LTQ_SUPPORT_SPL_SPI_FLASH
+ * - build a preloader that runs in the internal SRAM and loads
+ *   the U-Boot from SPI flash into RAM
+ */
+
+#ifndef __VRX200_CONFIG_H__
+#define __VRX200_CONFIG_H__
+
+/* CPU and SoC type */
+#define CONFIG_SOC_LANTIQ
+#define CONFIG_SOC_XWAY_VRX200
+
+/* Cache configuration */
+#define CONFIG_SYS_MIPS_CACHE_MODE	CONF_CM_CACHABLE_NONCOHERENT
+#define CONFIG_SYS_DCACHE_SIZE		(32 * 1024)
+#define CONFIG_SYS_ICACHE_SIZE		(32 * 1024)
+#define CONFIG_SYS_CACHELINE_SIZE	32
+#define CONFIG_SYS_MIPS_CACHE_EXT_INIT
+
+/*
+ * Supported clock modes
+ * PLL0 clock output is 1000 MHz
+ * PLL1 clock output is 393.219 MHz
+ */
+#define LTQ_CLK_CPU_600_DDR_300	0	/* Base PLL0, OCP 2 */
+#define LTQ_CLK_CPU_600_DDR_200	1	/* Base PLL0, OCP 3 */
+#define LTQ_CLK_CPU_500_DDR_250	2	/* Base PLL0, OCP 2 */
+#define LTQ_CLK_CPU_500_DDR_200	3	/* Base PLL0, OCP 2.5 */
+#define LTQ_CLK_CPU_333_DDR_167	4	/* Base PLL0, OCP 2 */
+#define LTQ_CLK_CPU_167_DDR_167	5	/* Base PLL0, OCP 1 */
+#define LTQ_CLK_CPU_125_DDR_125	6	/* Base PLL0, OCP 1 */
+#define LTQ_CLK_CPU_393_DDR_197	7	/* Base PLL1, OCP 2 */
+#define LTQ_CLK_CPU_197_DDR_197	8	/* Base PLL1, OCP 1 */
+
+/* CPU speed */
+#define CONFIG_SYS_CLOCK_MODE		LTQ_CLK_CPU_500_DDR_250
+#define CONFIG_SYS_MIPS_TIMER_FREQ	250000000
+#define CONFIG_SYS_HZ			1000
+
+/* RAM */
+#define CONFIG_NR_DRAM_BANKS		1
+#define CONFIG_SYS_SDRAM_BASE		0x80000000
+#define CONFIG_SYS_SDRAM_BASE_UC	0xa0000000
+#define CONFIG_SYS_MEMTEST_START	0x81000000
+#define CONFIG_SYS_MEMTEST_END		0x82000000
+#define CONFIG_SYS_LOAD_ADDR		0x81000000
+#define CONFIG_SYS_LOAD_SIZE		(2 * 1024 * 1024)
+#define CONFIG_SYS_INIT_SP_OFFSET	(32 * 1024)
+
+/* SRAM */
+#define CONFIG_SYS_SRAM_BASE		0xBE220000
+#define CONFIG_SYS_SRAM_SIZE		0x10000
+
+/* ASC/UART driver and console */
+#define CONFIG_LANTIQ_SERIAL
+#define CONFIG_SYS_BAUDRATE_TABLE	{ 9600, 19200, 38400, 57600, 115200 }
+
+/* GPIO */
+#define CONFIG_LANTIQ_GPIO
+#define CONFIG_LTQ_GPIO_MAX_BANKS	3
+#define CONFIG_LTQ_HAS_GPIO_BANK3
+
+/* FLASH driver */
+#if defined(CONFIG_LTQ_SUPPORT_NOR_FLASH)
+#ifndef CONFIG_SYS_MAX_FLASH_BANKS
+#define CONFIG_SYS_MAX_FLASH_BANKS	1
+#endif
+#define CONFIG_SYS_MAX_FLASH_SECT	256
+#define CONFIG_SYS_FLASH_BASE		0xB0000000
+#define CONFIG_SYS_FLASH2_BASE		0xB4000000
+#define CONFIG_FLASH_16BIT
+#define CONFIG_SYS_FLASH_CFI
+#define CONFIG_FLASH_CFI_DRIVER
+#define CONFIG_SYS_FLASH_CFI_WIDTH	FLASH_CFI_16BIT
+#define CONFIG_SYS_FLASH_USE_BUFFER_WRITE
+#define CONFIG_FLASH_SHOW_PROGRESS	50
+#define CONFIG_SYS_FLASH_PROTECTION
+#define CONFIG_CFI_FLASH_USE_WEAK_ADDR_SWAP
+
+#define CONFIG_CMD_FLASH
+#else
+#define CONFIG_SYS_NO_FLASH
+#endif /* CONFIG_NOR_FLASH */
+
+#if defined(CONFIG_LTQ_SUPPORT_SPI_FLASH)
+#define CONFIG_LANTIQ_SPI
+#define CONFIG_SPI_FLASH
+
+#define CONFIG_CMD_SF
+#define CONFIG_CMD_SPI
+#endif
+
+#if defined(CONFIG_LTQ_SUPPORT_NAND_FLASH)
+#define CONFIG_NAND_LANTIQ
+#define CONFIG_SYS_MAX_NAND_DEVICE	1
+#define CONFIG_SYS_NAND_BASE		0xB4000000
+
+#define CONFIG_CMD_NAND
+#endif
+
+#if defined(CONFIG_LTQ_SUPPORT_ETHERNET)
+#define CONFIG_LANTIQ_DMA
+#define CONFIG_LANTIQ_VRX200_SWITCH
+#define CONFIG_PHY_LANTIQ
+
+#define CONFIG_SYS_RX_ETH_BUFFER	8
+#define CONFIG_PHYLIB
+#define CONFIG_MII
+#define CONFIG_UDP_CHECKSUM
+
+#define CONFIG_CMD_MII
+#define CONFIG_CMD_NET
+#endif
+
+#define CONFIG_SPL_MAX_SIZE		(32 * 1024)
+#define CONFIG_SPL_BSS_MAX_SIZE		(8 * 1024)
+#define CONFIG_SPL_STACK_MAX_SIZE	(8 * 1024)
+#define CONFIG_SPL_MALLOC_MAX_SIZE	(32 * 1024)
+#define CONFIG_SPL_STACK_BSS_IN_SRAM
+
+#if defined(CONFIG_SPL_STACK_BSS_IN_SRAM)
+#define CONFIG_SPL_STACK_BASE		(CONFIG_SYS_SRAM_BASE + \
+					CONFIG_SPL_MAX_SIZE + \
+					CONFIG_SPL_STACK_MAX_SIZE - 1)
+#define CONFIG_SPL_BSS_BASE	  	(CONFIG_SPL_STACK_BASE + 1)
+#define CONFIG_SPL_MALLOC_BASE		(CONFIG_SYS_SDRAM_BASE + \
+					CONFIG_SYS_INIT_SP_OFFSET)
+#else
+#define CONFIG_SPL_STACK_BASE		(CONFIG_SYS_SDRAM_BASE + \
+					CONFIG_SYS_INIT_SP_OFFSET + \
+					CONFIG_SPL_STACK_MAX_SIZE - 1)
+#define CONFIG_SPL_BSS_BASE		(CONFIG_SPL_STACK_BASE + 1)
+#define CONFIG_SPL_MALLOC_BASE		(CONFIG_SPL_BSS_BASE + \
+					CONFIG_SPL_BSS_MAX_SIZE)
+#endif
+
+#if defined(CONFIG_SYS_BOOT_RAM)
+#define CONFIG_SYS_TEXT_BASE		0xA0100000
+#define CONFIG_SKIP_LOWLEVEL_INIT
+#define CONFIG_SYS_DISABLE_CACHE
+#endif
+
+#if defined(CONFIG_SYS_BOOT_NOR)
+#define CONFIG_SYS_TEXT_BASE		0xB0000000
+#endif
+
+#if defined(CONFIG_SYS_BOOT_SFSPL)
+#define CONFIG_SYS_TEXT_BASE		0x80100000
+#define CONFIG_SPL_TEXT_BASE		0xBE220000
+#endif
+
+#if defined(CONFIG_SYS_BOOT_NORSPL)
+#define CONFIG_SYS_TEXT_BASE		0x80100000
+#define CONFIG_SPL_TEXT_BASE		0xB0000000
+#endif
+
+#if defined(CONFIG_SYS_BOOT_NOR) || defined(CONFIG_SYS_BOOT_NORSPL)
+#define CONFIG_SYS_XWAY_EBU_BOOTCFG	0x688C688C
+#define CONFIG_XWAY_SWAP_BYTES
+#endif
+
+#define	CONFIG_SYS_MONITOR_BASE		CONFIG_SYS_TEXT_BASE
+
+#endif /* __VRX200_CONFIG_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/arch-vrx200/gphy.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __VRX200_GPHY_H__
+#define __VRX200_GPHY_H__
+
+enum ltq_gphy_clk {
+	/* XTAL 36 MHz input */
+	LTQ_GPHY_CLK_36MHZ_XTAL = 1,
+	/* 25 MHz from PLL0 with divider */
+	LTQ_GPHY_CLK_25MHZ_PLL0 = 2,
+	/* derived from PLL2 output (XTAL is 36 MHz) */
+	LTQ_GPHY_CLK_24MHZ_PLL2 = 3,
+	/* 25 MHz Clock from Pin GPIO3 */
+	LTQ_GPHY_CLK_25MHZ_GPIO3 = 4,
+};
+
+/*
+ * Load PHY11G firmware for VRX200 v1.1 to given RAM address
+ *
+ * Address must be 16k aligned!
+ */
+extern void ltq_gphy_phy11g_a1x_load(ulong addr);
+
+/*
+ * Load PHY11G firmware for VRX200 v1.2 to given RAM address
+ *
+ * Address must be 16k aligned!
+ */
+extern void ltq_gphy_phy11g_a2x_load(ulong addr);
+
+/*
+ * Load PHY22F firmware for VRX200 v1.1 to given RAM address
+ *
+ * Address must be 16k aligned!
+ */
+extern void ltq_gphy_phy22f_a1x_load(ulong addr);
+
+/*
+ * Load PHY22F firmware for VRX200 v1.2 to given RAM address
+ *
+ * Address must be 16k aligned!
+ */
+extern void ltq_gphy_phy22f_a2x_load(ulong addr);
+
+/*
+ * Set clock source of internal GPHYs
+ *
+ * According registers resides in CGU address space. Thus this function
+ * is implemented by the CGU driver.
+ */
+extern void ltq_cgu_gphy_clk_src(enum ltq_gphy_clk clk);
+
+/*
+ * Boot internal GPHY with id from given RAM address
+ *
+ * According registers resides in RCU address space. Thus this function
+ * is implemented by the RCU driver.
+ */
+extern void ltq_rcu_gphy_boot(unsigned int id, ulong addr);
+
+#endif /* __VRX200_GPHY_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/arch-vrx200/gpio.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __VRX200_GPIO_H__
+#define __VRX200_GPIO_H__
+
+#include <asm/lantiq/gpio.h>
+
+#endif /* __VRX200_GPIO_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/arch-vrx200/nand.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) 2012-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __VRX200_NAND_H__
+#define __VRX200_NAND_H__
+
+struct nand_chip;
+int ltq_nand_init(struct nand_chip *nand);
+
+#endif /* __VRX200_NAND_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/arch-vrx200/soc.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __VRX200_SOC_H__
+#define __VRX200_SOC_H__
+
+#define LTQ_ASC0_BASE			0x1E100400
+#define LTQ_SPI_BASE			0x1E100800
+#define LTQ_GPIO_BASE			0x1E100B00
+#define LTQ_SSIO_BASE			0x1E100BB0
+#define LTQ_ASC1_BASE			0x1E100C00
+#define LTQ_DMA_BASE			0x1E104100
+
+#define LTQ_EBU_BASE			0x1E105300
+#define LTQ_EBU_REGION0_BASE		0x10000000
+#define LTQ_EBU_REGION1_BASE		0x14000000
+#define LTQ_EBU_NAND_BASE		(LTQ_EBU_BASE + 0xB0)
+
+#define LTQ_SWITCH_BASE			0x1E108000
+#define LTQ_SWITCH_CORE_BASE		LTQ_SWITCH_BASE
+#define LTQ_SWITCH_TOP_PDI_BASE		LTQ_SWITCH_CORE_BASE
+#define LTQ_SWITCH_BM_PDI_BASE		(LTQ_SWITCH_CORE_BASE + 4 * 0x40)
+#define LTQ_SWITCH_MAC_PDI_0_BASE	(LTQ_SWITCH_CORE_BASE + 4 * 0x900)
+#define LTQ_SWITCH_MAC_PDI_X_BASE(x)	(LTQ_SWITCH_MAC_PDI_0_BASE + x * 0x30)
+#define LTQ_SWITCH_TOPLEVEL_BASE	(LTQ_SWITCH_BASE + 4 * 0xC40)
+#define LTQ_SWITCH_MDIO_PDI_BASE	(LTQ_SWITCH_TOPLEVEL_BASE)
+#define LTQ_SWITCH_MII_PDI_BASE		(LTQ_SWITCH_TOPLEVEL_BASE + 4 * 0x36)
+#define LTQ_SWITCH_PMAC_PDI_BASE	(LTQ_SWITCH_TOPLEVEL_BASE + 4 * 0x82)
+
+#define LTQ_PMU_BASE			0x1F102000
+#define LTQ_CGU_BASE			0x1F103000
+#define LTQ_DCDC_BASE			0x1F106A00
+#define LTQ_MPS_BASE			0x1F107000
+#define LTQ_CHIPID_BASE			(LTQ_MPS_BASE + 0x340)
+#define LTQ_RCU_BASE			0x1F203000
+
+#define LTQ_MC_GLOBAL_BASE		0x1F400000
+#define LTQ_MC_DDR_BASE			0x1F401000
+#define LTQ_MC_DDR_CCR_OFFSET(x)	(x * 0x10)
+
+#endif /* __VRX200_SOC_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/arch-vrx200/switch.h
@@ -0,0 +1,502 @@
+/*
+ *   Copyright (C) 2012-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ *   SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __VRX200_SWITCH_H__
+#define __VRX200_SWITCH_H__
+
+/* Switch core registers */
+struct vr9_switch_core_regs {
+	__be32 swres;
+	/* TODO: implement registers */
+	__be32 rsvd0[0x3f];
+};
+
+/* Switch buffer management registers */
+struct vr9_switch_bm_regs {
+	struct bm_core {
+		__be32 ram_val3;	/* RAM value 3 */
+		__be32 ram_val2;	/* RAM value 2 */
+		__be32 ram_val1;	/* RAM value 1 */
+		__be32 ram_val0;	/* RAM value 0 */
+		__be32 ram_addr;	/* RAM address */
+		__be32 ram_ctrl;	/* RAM access control */
+		__be32 fsqm_gctrl;	/* Free segment queue global control */
+		__be32 cons_sel;	/* Number of consumed segments */
+		__be32 cons_pkt;	/* Number of consumed packet pointers */
+		__be32 gctrl;		/* Global control */
+		__be32 queue_gctrl;	/* Queue manager global control */
+		/* TODO: implement registers */
+		__be32 rsvd0[0x35];
+	} core;
+
+	struct bm_port {
+		__be32 pcfg;		/* Port config */
+		__be32 rmon_ctrl;	/* RMON control */
+	} port[13];
+
+	__be32 rsvd0[0x66];
+
+	struct bm_queue {
+		__be32 rsvd0;
+		__be32 pqm_rs;		/* Packet queue manager rate shape assignment */
+	} queue[32];
+
+	struct bm_shaper {
+		__be32 ctrl;		/* Rate shaper control */
+		__be32 cbs;		/* Rate shaper committed burst size */
+		__be32 ibs;		/* Rate shaper instantaneous burst size */
+		__be32 cir_ext;		/* Rate shaper rate exponent */
+		__be32 cir_mant;	/* Rate shaper rate mantissa */
+	} shaper[16];
+
+	__be32 rsvd1[0x2a8];
+};
+
+/* Switch parser and classification engine registers */
+struct vr9_switch_pce_regs {
+	struct pce_core {
+		__be32 tbl_key[16];	/* Table key data */
+		__be32 tbl_mask;	/* Table mask */
+		__be32 tbl_val[5];	/* Table value */
+		__be32 tbl_addr;	/* Table entry address */
+		__be32 tbl_ctrl;	/* Table access control */
+		__be32 tbl_stat;	/* Table general status */
+		__be32 age_0;		/* Aging counter config 0 */
+		__be32 age_1;		/* Aging counter config 1 */
+		__be32 pmap_1;		/* Port map (monitoring) */
+		__be32 pmap_2;		/* Port map (multicast) */
+		__be32 pmap_3;		/* Port map (unknown unicast) */
+		__be32 gctrl_0;		/* Global control 0 */
+		__be32 gctrl_1;		/* Global control 1 */
+		__be32 tcm_gctrl;	/* Three-color marker global control */
+		__be32 igmp_ctrl;	/* IGMP control */
+		__be32 igmp_drpm;	/* IGMP default router port map */
+		__be32 igmp_age_0;	/* IGMP aging 0 */
+		__be32 igmp_age_1;	/* IGMP aging 1 */
+		__be32 igmp_stat;	/* IGMP status */
+		__be32 wol_gctrl;	/* Wake-on-LAN control */
+		__be32 wol_da_0;	/* Wake-on-LAN destination address 0 */
+		__be32 wol_da_1;	/* Wake-on-LAN destination address 1 */
+		__be32 wol_da_2;	/* Wake-on-LAN destination address 2 */
+		__be32 wol_pw_0;	/* Wake-on-LAN password 0 */
+		__be32 wol_pw_1;	/* Wake-on-LAN password 1 */
+		__be32 wol_pw_2;	/* Wake-on-LAN password 2 */
+		__be32 ier_0;		/* PCE global interrupt enable 0 */
+		__be32 ier_1;		/* PCE global interrupt enable 1 */
+		__be32 isr_0;		/* PCE global interrupt status 0 */
+		__be32 isr_1;		/* PCE global interrupt status 1 */
+		__be32 parser_stat;	/* Parser status */
+		__be32 rsvd0[0x6];
+	} core;
+
+	__be32 rsvd0[0x10];
+
+	struct pce_port {
+		__be32 pctrl_0;		/* Port control 0 */
+		__be32 pctrl_1;		/* Port control 1 */
+		__be32 pctrl_2;		/* Port control 2 */
+		__be32 pctrl_3;		/* Port control 3 */
+		__be32 wol_ctrl;	/* Wake-on-LAN control */
+		__be32 vlan_ctrl;	/* VLAN control */
+		__be32 def_pvid;	/* Default port VID */
+		__be32 pstat;		/* Port status */
+		__be32 pier;		/* Interrupt enable */
+		__be32 pisr;		/* Interrupt status */
+	} port[13];
+
+	__be32 rsvd1[0x7e];
+
+	struct pce_meter {
+		/* TODO: implement registers */
+		__be32 rsvd0[0x7];
+	} meter[8];
+
+	__be32 rsvd2[0x308];
+};
+
+static inline unsigned int to_pce_tbl_key_id(unsigned int id)
+{
+	BUG_ON(id > 15);
+
+	return 15 - id;
+}
+
+static inline unsigned int to_pce_tbl_value_id(unsigned int id)
+{
+	BUG_ON(id > 4);
+
+	return 4 - id;
+}
+
+/* Switch ethernet MAC registers */
+struct vr9_switch_mac_regs {
+	struct mac_core {
+		__be32 test;		/* MAC test */
+		__be32 pfad_cfg;	/* Pause frame source address config */
+		__be32 pfsa_0;		/* Pause frame source address 0 */
+		__be32 pfsa_1;		/* Pause frame source address 1 */
+		__be32 pfsa_2;		/* Pause frame source address 2 */
+		__be32 flen;		/* Frame length */
+		__be32 vlan_etype_0;	/* VLAN ethertype 0 */
+		__be32 vlan_etype_1;	/* VLAN ethertype 1 */
+		__be32 ier;		/* Interrupt enable */
+		__be32 isr;		/* Interrupt status */
+		__be32 rsvd0[0x36];
+	} core;
+
+	struct mac_port {
+		__be32 pstat;		/* Port status */
+		__be32 pisr;		/* Interrupt status */
+		__be32 pier;		/* Interrupt enable */
+		__be32 ctrl_0;		/* Control 0 */
+		__be32 ctrl_1;		/* Control 1 */
+		__be32 ctrl_2;		/* Control 2 */
+		__be32 ctrl_3;		/* Control 3 */
+		__be32 ctrl_4;		/* Control 4 */
+		__be32 ctrl_5;		/* Control 5 */
+		__be32 rsvd0[0x2];
+		__be32 testen;		/* Test enable */
+	} port[13];
+
+	__be32 rsvd0[0xa4];
+};
+
+/* Switch Fetch DMA registers */
+struct vr9_switch_fdma_regs {
+	struct fdma_core {
+		__be32 ctrl;		/* FDMA control */
+		__be32 stetype;		/* Special tag ethertype control */
+		__be32 vtetype;		/* VLAN tag ethertype control */
+		__be32 stat;		/* FDMA status */
+		__be32 ier;		/* FDMA interrupt enable */
+		__be32 isr;		/* FDMA interrupt status */
+	} core;
+
+	__be32 rsvd0[0x3a];
+
+	struct fdma_port {
+		__be32 pctrl;		/* Port control */
+		__be32 prio;		/* Port priority */
+		__be32 pstat_0;		/* Port status 0 */
+		__be32 pstat_1;		/* Port status 1 */
+		__be32 tstamp_0;	/* Egress time stamp 0 */
+		__be32 tstamp_1;	/* Egress time stamp 1 */
+	} port[13];
+
+	__be32 rsvd1[0x72];
+};
+
+/* Switch Store DMA registers */
+struct vr9_switch_sdma_regs {
+	struct sdma_core {
+		__be32 ctrl;		/* SDMA Control */
+		__be32 fcthr_1;		/* Flow control threshold 1 */
+		__be32 rsvd0;
+		__be32 fcthr_3;		/* Flow control threshold 3 */
+		__be32 fcthr_4;		/* Flow control threshold 4 */
+		__be32 fcthr_5;		/* Flow control threshold 5 */
+		__be32 fcthr_6;		/* Flow control threshold 6 */
+		__be32 fcthr_7;		/* Flow control threshold 7 */
+		__be32 stat_0;		/* SDMA status 0 */
+		__be32 stat_1;		/* SDMA status 1 */
+		__be32 stat_2;		/* SDMA status 2 */
+		__be32 ier;		/* SDMA interrupt enable */
+		__be32 isr;		/* SDMA interrupt status */
+	} core;
+
+	__be32 rsvd0[0x73];
+
+	struct sdma_port {
+		__be32 pctrl;		/* Port control */
+		__be32 prio;		/* Port priority */
+		__be32 pstat_0;		/* Port status 0 */
+		__be32 pstat_1;		/* Port status 1 */
+		__be32 tstamp_0;	/* Ingress time stamp 0 */
+		__be32 tstamp_1;	/* Ingress time stamp 1 */
+	} port[13];
+
+	__be32 rsvd1[0x32];
+};
+
+/* Switch MDIO control and status registers */
+struct vr9_switch_mdio_regs {
+	__be32 glob_ctrl;	/* Global control 0 */
+	__be32 rsvd0[7];
+	__be32 mdio_ctrl;	/* MDIO control */
+	__be32 mdio_read;	/* MDIO read data */
+	__be32 mdio_write;	/* MDIO write data */
+	__be32 mdc_cfg_0;	/* MDC clock configuration 0 */
+	__be32 mdc_cfg_1;	/* MDC clock configuration 1 */
+	__be32 rsvd1[0x3];
+	__be32 phy_addr[6];	/* PHY address port 5..0 */
+	__be32 mdio_stat[6];	/* MDIO PHY polling status port 0..5 */
+	__be32 aneg_eee[6];	/* EEE auto-neg overrides port 0..5 */
+	__be32 rsvd2[0x14];
+};
+
+static inline unsigned int to_mdio_phyaddr_id(unsigned int id)
+{
+	BUG_ON(id > 5);
+
+	return 5 - id;
+}
+
+/* Switch xMII control registers */
+struct vr9_switch_mii_regs {
+	__be32 mii_cfg0;	/* xMII port 0 configuration */
+	__be32 pcdu0;		/* Port 0 clock delay configuration */
+	__be32 mii_cfg1;	/* xMII port 1 configuration */
+	__be32 pcdu1;		/* Port 1 clock delay configuration */
+	__be32 rsvd0[0x6];
+	__be32 mii_cfg5;	/* xMII port 5 configuration */
+	__be32 pcdu5;		/* Port 5 clock delay configuration */
+	__be32 rsvd1[0x14];
+	__be32 rxb_ctl_0;	/* Port 0 receive buffer control */
+	__be32 rxb_ctl_1;	/* Port 1 receive buffer control */
+	__be32 rxb_ctl_5;	/* Port 5 receive buffer control */
+	__be32 rsvd2[0x28];
+	__be32 dbg_ctl;		/* Debug control */
+};
+
+/* Switch Pseudo-MAC registers */
+struct vr9_switch_pmac_regs {
+	__be32 hd_ctl;		/* PMAC header control */
+	__be32 tl;		/* PMAC type/length */
+	__be32 sa1;		/* PMAC source address 1 */
+	__be32 sa2;		/* PMAC source address 2 */
+	__be32 sa3;		/* PMAC source address 3 */
+	__be32 da1;		/* PMAC destination address 1 */
+	__be32 da2;		/* PMAC destination address 2 */
+	__be32 da3;		/* PMAC destination address 3 */
+	__be32 vlan;		/* PMAC VLAN */
+	__be32 rx_ipg;		/* PMAC interpacket gap in RX direction */
+	__be32 st_etype;	/* PMAC special tag ethertype */
+	__be32 ewan;		/* PMAC ethernet WAN group */
+	__be32 ctl;		/* PMAC control */
+	__be32 rsvd0[0x2];
+};
+
+struct vr9_switch_regs {
+	struct vr9_switch_core_regs core;
+	struct vr9_switch_bm_regs bm;
+	struct vr9_switch_pce_regs pce;
+	struct vr9_switch_mac_regs mac;
+	struct vr9_switch_fdma_regs fdma;
+	struct vr9_switch_sdma_regs sdma;
+	struct vr9_switch_mdio_regs mdio;
+	struct vr9_switch_mii_regs mii;
+	struct vr9_switch_pmac_regs pmac;
+};
+
+static inline void *to_pce_tbl_key(struct vr9_switch_regs *regs,
+						unsigned int id)
+{
+	return &regs->pce.core.tbl_key[to_pce_tbl_key_id(id)];
+}
+
+static inline void *to_pce_tbl_value(struct vr9_switch_regs *regs,
+						unsigned int id)
+{
+	return &regs->pce.core.tbl_val[to_pce_tbl_value_id(id)];
+}
+
+static inline void *to_mac_ctrl(struct vr9_switch_regs *regs,
+					unsigned int id, unsigned int ctrl)
+{
+	struct mac_port *mac = &regs->mac.port[id];
+
+	switch (ctrl) {
+	case 0:
+		return &mac->ctrl_0;
+	case 1:
+		return &mac->ctrl_1;
+	case 2:
+		return &mac->ctrl_2;
+	case 3:
+		return &mac->ctrl_3;
+	case 4:
+		return &mac->ctrl_4;
+	case 5:
+		return &mac->ctrl_5;
+	default:
+		return NULL;
+	}
+}
+
+static inline void *to_mdio_phyaddr(struct vr9_switch_regs *regs,
+					unsigned int id)
+{
+	return &regs->mdio.phy_addr[to_mdio_phyaddr_id(id)];
+}
+
+static inline void *to_mii_miicfg(struct vr9_switch_regs *regs,
+					unsigned int id)
+{
+	switch (id) {
+	case 0:
+		return &regs->mii.mii_cfg0;
+	case 1:
+		return &regs->mii.mii_cfg1;
+	case 5:
+		return &regs->mii.mii_cfg5;
+	default:
+		return NULL;
+	}
+}
+
+static inline void *to_mii_pcdu(struct vr9_switch_regs *regs,
+					unsigned int id)
+{
+	switch (id) {
+	case 0:
+		return &regs->mii.pcdu0;
+	case 1:
+		return &regs->mii.pcdu1;
+	case 5:
+		return &regs->mii.pcdu5;
+	default:
+		return NULL;
+	}
+}
+
+#define VR9_SWITCH_REG_OFFSET(reg)	(4 * (reg))
+
+#define BUILD_CHECK_VR9_REG(name, offset)	\
+	BUILD_BUG_ON(offsetof(struct vr9_switch_regs, name) != (4 * offset))
+
+static inline void build_check_vr9_registers(void)
+{
+	BUILD_CHECK_VR9_REG(core, 0x0);
+	BUILD_CHECK_VR9_REG(bm.core, 0x40);
+	BUILD_CHECK_VR9_REG(bm.core.queue_gctrl, 0x4a);
+	BUILD_CHECK_VR9_REG(bm.port[0], 0x80);
+	BUILD_CHECK_VR9_REG(bm.queue, 0x100);
+	BUILD_CHECK_VR9_REG(bm.shaper, 0x140);
+	BUILD_CHECK_VR9_REG(pce.core, 0x438);
+	BUILD_CHECK_VR9_REG(pce.core.tbl_ctrl, 0x44f);
+	BUILD_CHECK_VR9_REG(pce.core.parser_stat, 0x469);
+	BUILD_CHECK_VR9_REG(pce.port[0], 0x480);
+	BUILD_CHECK_VR9_REG(pce.meter[0], 0x580);
+	BUILD_CHECK_VR9_REG(mac.core, 0x8c0);
+	BUILD_CHECK_VR9_REG(mac.port[0].pstat, 0x900);
+	BUILD_CHECK_VR9_REG(mac.port[0].ctrl_0, 0x903);
+	BUILD_CHECK_VR9_REG(mac.port[1].pstat, 0x90c);
+	BUILD_CHECK_VR9_REG(mac.port[1].ctrl_0, 0x90f);
+	BUILD_CHECK_VR9_REG(mac.port[2].pstat, 0x918);
+	BUILD_CHECK_VR9_REG(mac.port[2].ctrl_0, 0x91b);
+	BUILD_CHECK_VR9_REG(fdma.core, 0xa40);
+	BUILD_CHECK_VR9_REG(fdma.port[0], 0xa80);
+	BUILD_CHECK_VR9_REG(sdma.core, 0xb40);
+	BUILD_CHECK_VR9_REG(sdma.port[0], 0xbc0);
+	BUILD_CHECK_VR9_REG(mdio, 0xc40);
+	BUILD_CHECK_VR9_REG(mii, (0xc40 + 0x36));
+	BUILD_CHECK_VR9_REG(pmac, (0xc40 + 0x82));
+}
+
+#define BM_GCTRL_F_SRES		1
+
+#define MAC_CTRL0_BM		(1 << 12)
+#define MAC_CTRL0_APADEN	(1 << 11)
+#define MAC_CTRL0_VPAD2EN	(1 << 10)
+#define MAC_CTRL0_VPADEN	(1 << 9)
+#define MAC_CTRL0_PADEN		(1 << 8)
+#define MAC_CTRL0_FCS		(1 << 7)
+#define MAC_CTRL0_FCON_SHIFT	4
+#define MAC_CTRL0_FCON_AUTO	(0x0 << MAC_CTRL0_FCON_SHIFT)
+#define MAC_CTRL0_FCON_RX	(0x1 << MAC_CTRL0_FCON_SHIFT)
+#define MAC_CTRL0_FCON_TX	(0x2 << MAC_CTRL0_FCON_SHIFT)
+#define MAC_CTRL0_FCON_RXTX	(0x3 << MAC_CTRL0_FCON_SHIFT)
+#define MAC_CTRL0_FCON_NONE	(0x4 << MAC_CTRL0_FCON_SHIFT)
+#define MAC_CTRL0_FDUP_SHIFT	2
+#define MAC_CTRL0_FDUP_AUTO	(0x0 << MAC_CTRL0_FDUP_SHIFT)
+#define MAC_CTRL0_FDUP_EN	(0x1 << MAC_CTRL0_FDUP_SHIFT)
+#define MAC_CTRL0_FDUP_DIS	(0x3 << MAC_CTRL0_FDUP_SHIFT)
+#define MAC_CTRL0_GMII_AUTO	0x0
+#define MAC_CTRL0_GMII_MII	0x1
+#define MAC_CTRL0_GMII_GMII	0x2
+#define MAC_CTRL0_GMII_GMII_2G	0x3
+
+#define MAC_CTRL1_DEFERMODE	(1 << 15)
+#define MAC_CTRL1_SHORTPRE	(1 << 8)
+
+#define MAC_CTRL2_MLEN		(1 << 3)
+#define MAC_CTRL2_LCHKL		(1 << 2)
+#define MAC_CTRL2_LCHKS_DIS	0x0
+#define MAC_CTRL2_LCHKS_UNTAG	0x1
+#define MAC_CTRL2_LCHKS_TAG	0x2
+
+#define PHY_ADDR_LNKST_SHIFT	13
+#define PHY_ADDR_LNKST_AUTO	(0x0 << PHY_ADDR_LNKST_SHIFT)
+#define PHY_ADDR_LNKST_UP	(0x1 << PHY_ADDR_LNKST_SHIFT)
+#define PHY_ADDR_LNKST_DOWN	(0x2 << PHY_ADDR_LNKST_SHIFT)
+#define PHY_ADDR_SPEED_SHIFT	11
+#define PHY_ADDR_SPEED_M10	(0x0 << PHY_ADDR_SPEED_SHIFT)
+#define PHY_ADDR_SPEED_M100	(0x1 << PHY_ADDR_SPEED_SHIFT)
+#define PHY_ADDR_SPEED_G1	(0x2 << PHY_ADDR_SPEED_SHIFT)
+#define PHY_ADDR_SPEED_AUTO	(0x3 << PHY_ADDR_SPEED_SHIFT)
+#define PHY_ADDR_FDUP_SHIFT	9
+#define PHY_ADDR_FDUP_AUTO	(0x0 << PHY_ADDR_FDUP_SHIFT)
+#define PHY_ADDR_FDUP_EN	(0x1 << PHY_ADDR_FDUP_SHIFT)
+#define PHY_ADDR_FDUP_DIS	(0x3 << PHY_ADDR_FDUP_SHIFT)
+#define PHY_ADDR_FCONTX_SHIFT	7
+#define PHY_ADDR_FCONTX_AUTO	(0x0 << PHY_ADDR_FCONTX_SHIFT)
+#define PHY_ADDR_FCONTX_EN	(0x1 << PHY_ADDR_FCONTX_SHIFT)
+#define PHY_ADDR_FCONTX_DIS	(0x3 << PHY_ADDR_FCONTX_SHIFT)
+#define PHY_ADDR_FCONRX_SHIFT	5
+#define PHY_ADDR_FCONRX_AUTO	(0x0 << PHY_ADDR_FCONRX_SHIFT)
+#define PHY_ADDR_FCONRX_EN	(0x1 << PHY_ADDR_FCONRX_SHIFT)
+#define PHY_ADDR_FCONRX_DIS	(0x3 << PHY_ADDR_FCONRX_SHIFT)
+
+#define MII_CFG_RES		(1 << 15)
+#define MII_CFG_EN		(1 << 14)
+#define MII_CFG_LDCLKDIS	(1 << 12)
+#define MII_CFG_MIIRATE_SHIFT	4
+#define MII_CFG_MIIRATE_MASK	(0x7 << MII_CFG_MIIRATE_SHIFT)
+#define MII_CFG_MIIRATE_M2P5	(0x0 << MII_CFG_MIIRATE_SHIFT)
+#define MII_CFG_MIIRATE_M25	(0x1 << MII_CFG_MIIRATE_SHIFT)
+#define MII_CFG_MIIRATE_M125	(0x2 << MII_CFG_MIIRATE_SHIFT)
+#define MII_CFG_MIIRATE_M50	(0x3 << MII_CFG_MIIRATE_SHIFT)
+#define MII_CFG_MIIRATE_AUTO	(0x4 << MII_CFG_MIIRATE_SHIFT)
+#define MII_CFG_MIIMODE_MASK	0xf
+#define MII_CFG_MIIMODE_MIIP	0x0
+#define MII_CFG_MIIMODE_MIIM	0x1
+#define MII_CFG_MIIMODE_RMIIP	0x2
+#define MII_CFG_MIIMODE_RMIIM	0x3
+#define MII_CFG_MIIMODE_RGMII	0x4
+
+#define PCDU_RXDLY_SHIFT	7
+#define PCDU_RXDLY_MASK		(0x7 << PCDU_RXDLY_SHIFT)
+#define PCDU_TXDLY_MASK		0x7
+
+#define PMAC_HD_CTL_FC		(1 << 10)
+#define PMAC_HD_CTL_CCRC	(1 << 9)
+#define PMAC_HD_CTL_RST		(1 << 8)
+#define PMAC_HD_CTL_AST		(1 << 7)
+#define PMAC_HD_CTL_RXSH	(1 << 6)
+#define PMAC_HD_CTL_RC		(1 << 4)
+#define PMAC_HD_CTL_AS		(1 << 3)
+#define PMAC_HD_CTL_AC		(1 << 2)
+
+#define PCE_PCTRL_0_IGSTEN	(1 << 11)
+
+#define FDMA_PCTRL_STEN		(1 << 1)
+#define FDMA_PCTRL_EN		(1 << 0)
+
+#define SDMA_PCTRL_EN		(1 << 0)
+
+#define MDIO_GLOB_CTRL_SE	(1 << 15)
+
+#define MDIO_MDC_CFG1_RES	(1 << 15)
+#define MDIO_MDC_CFG1_MCEN	(1 << 8)
+
+#define MDIO_CTRL_MBUSY		(1 << 12)
+#define MDIO_CTRL_OP_READ	(1 << 11)
+#define MDIO_CTRL_OP_WRITE	(1 << 10)
+#define MDIO_CTRL_PHYAD_SHIFT	5
+#define MDIO_CTRL_PHYAD_MASK	(0x1f << MDIO_CTRL_PHYAD_SHIFT)
+#define MDIO_CTRL_REGAD_MASK	0x1f
+
+#endif /* __VRX200_SWITCH_H__ */
--- a/arch/mips/include/asm/asm.h
+++ b/arch/mips/include/asm/asm.h
@@ -53,6 +53,7 @@
 		.align	2;                              \
 		.type	symbol, @function;              \
 		.ent	symbol, 0;                      \
+		.section .text.symbol,"x";              \
 symbol:		.frame	sp, 0, ra
 
 /*
@@ -62,7 +63,8 @@ symbol:		.frame	sp, 0, ra
 		.globl	symbol;                         \
 		.align	2;                              \
 		.type	symbol, @function;              \
-		.ent	symbol, 0;                       \
+		.ent	symbol, 0;                      \
+		.section .text.symbol,"x";              \
 symbol:		.frame	sp, framesize, rpc
 
 /*
--- /dev/null
+++ b/arch/mips/include/asm/gpio.h
@@ -0,0 +1,6 @@
+/*
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <asm/arch/gpio.h>
+#include <asm-generic/gpio.h>
--- /dev/null
+++ b/arch/mips/include/asm/lantiq/chipid.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LANTIQ_CHIPID_H__
+#define __LANTIQ_CHIPID_H__
+
+enum ltq_chip_partnum {
+	LTQ_SOC_UNKNOWN = 0,
+	LTQ_SOC_VRX288_2 = 0x000B,	/* VRX288 v1.2 */
+	LTQ_SOC_VRX268_2 = 0x000C,	/* VRX268 v1.2 */
+	LTQ_SOC_GRX288_2 = 0x000D,	/* GRX288 v1.2 */
+	LTQ_SOC_DANUBE = 0x0129,
+	LTQ_SOC_DANUBE_S = 0x012B,
+	LTQ_SOC_TWINPASS = 0x012D,
+	LTQ_SOC_VRX288 = 0x01C0,	/* VRX288 v1.1 */
+	LTQ_SOC_VRX268 = 0x01C2,	/* VRX268 v1.1 */
+	LTQ_SOC_GRX288 = 0x01C9,	/* GRX288 v1.1 */
+};
+
+extern unsigned int ltq_chip_version_get(void);
+extern unsigned int ltq_chip_partnum_get(void);
+extern const char *ltq_chip_partnum_str(void);
+
+extern void ltq_chip_print_info(void);
+
+#ifdef CONFIG_SOC_XWAY_DANUBE
+static inline int ltq_soc_is_danube(void)
+{
+	return 1;
+}
+#else
+static inline int ltq_soc_is_danube(void)
+{
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_SOC_XWAY_VRX200
+static inline int ltq_soc_is_vrx200(void)
+{
+	return 1;
+}
+
+static inline int ltq_soc_is_vrx200_v1(void)
+{
+	return ltq_chip_version_get() == 1;
+}
+
+static inline int ltq_soc_is_vrx200_v2(void)
+{
+	return ltq_chip_version_get() == 2;
+}
+#else
+static inline int ltq_soc_is_vrx200(void)
+{
+	return 0;
+}
+
+static inline int ltq_soc_is_vrx200_v1(void)
+{
+	return 0;
+}
+
+static inline int ltq_soc_is_vrx200_v2(void)
+{
+	return 0;
+}
+#endif
+
+#endif /* __LANTIQ_CHIPID_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/lantiq/clk.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007-2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ * *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LANTIQ_CLK_H__
+#define __LANTIQ_CLK_H__
+
+/* Symbolic clock speeds */
+enum ltq_clk {
+	CLOCK_83_MHZ = 83333333,
+	CLOCK_111_MHZ = 111111111,
+	CLOCK_125_MHZ = 125000000,
+	CLOCK_133_MHZ = 133333333,
+	CLOCK_166_MHZ = 166666667,
+	CLOCK_197_MHZ = 197000000,
+	CLOCK_333_MHZ = 333333333,
+	CLOCK_393_MHZ = 393219000,
+	CLOCK_500_MHZ = 500000000,
+	CLOCK_600_MHZ = 600000000,
+	CLOCK_1000_MHZ = 1000000000,
+};
+
+extern unsigned long ltq_get_cpu_clock(void);
+extern unsigned long ltq_get_bus_clock(void);
+extern unsigned long ltq_get_io_region_clock(void);
+
+#endif /* __LANTIQ_CLK_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/lantiq/config.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007-2010 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LANTIQ_CONFIG_H__
+#define __LANTIQ_CONFIG_H__
+
+/* Memory usage */
+#define CONFIG_SYS_MAXARGS		24
+#define CONFIG_SYS_MALLOC_LEN		1024*1024
+#define CONFIG_SYS_BOOTPARAMS_LEN	128*1024
+
+/* Command line */
+#define CONFIG_SYS_PROMPT		CONFIG_MACH_TYPE " # "
+#define CONFIG_SYS_CBSIZE		512
+#define CONFIG_SYS_PBSIZE		(CONFIG_SYS_CBSIZE + \
+					sizeof(CONFIG_SYS_PROMPT)+16)
+
+#define CONFIG_SYS_HUSH_PARSER
+#define CONFIG_SYS_PROMPT_HUSH_PS2	"> "
+
+/*
+ * Enable advanced console features on demand to reduce
+ * flash and RAM footprint
+ */
+#if defined(CONFIG_LTQ_ADVANCED_CONSOLE)
+#define CONFIG_SYS_LONGHELP
+#define CONFIG_AUTO_COMPLETE
+#define CONFIG_CMDLINE_EDITING
+#endif
+
+/* SPI flash SPL */
+#if defined(CONFIG_LTQ_SUPPORT_SPL_SPI_FLASH) && defined(CONFIG_SYS_BOOT_SFSPL)
+#define CONFIG_SPL
+#define CONFIG_SPL_SPI_SUPPORT
+#define CONFIG_SPL_SPI_FLASH_SUPPORT
+#define CONFIG_SPI_SPL_SIMPLE
+#endif
+
+#if defined(CONFIG_LTQ_SUPPORT_SPL_NOR_FLASH) && defined(CONFIG_SYS_BOOT_NORSPL)
+#define CONFIG_SPL
+#endif
+
+/* Common SPL */
+#if defined(CONFIG_SPL)
+#define CONFIG_SKIP_LOWLEVEL_INIT
+#define CONFIG_SPL_LIBGENERIC_SUPPORT
+#define CONFIG_SPL_GPIO_SUPPORT
+#define CONFIG_SPL_START_S_PATH		\
+		"arch/mips/cpu/mips32/lantiq-common"
+#define CONFIG_SPL_LDSCRIPT		\
+		"arch/mips/cpu/mips32/lantiq-common/u-boot-spl.lds"
+#endif
+
+#if defined(CONFIG_LTQ_SPL_CONSOLE)
+#define CONFIG_SPL_SERIAL_SUPPORT
+#define CONFIG_SPL_LIBCOMMON_SUPPORT
+#endif
+
+#if defined(CONFIG_LTQ_SPL_COMP_LZMA)
+#define CONFIG_LZMA
+#define CONFIG_SPL_LZMA_SUPPORT
+#endif
+
+#if defined(CONFIG_LTQ_SPL_COMP_LZO)
+#define CONFIG_LZO
+#define CONFIG_SPL_LZO_SUPPORT
+#endif
+
+/* Basic commands */
+#define CONFIG_CMD_BDI
+#define CONFIG_CMD_EDITENV
+#define CONFIG_CMD_IMI
+#define CONFIG_CMD_MEMORY
+#define CONFIG_CMD_RUN
+#define CONFIG_CMD_SAVEENV
+#define CONFIG_CMD_LOADB
+
+/* Other U-Boot settings */
+#define CONFIG_TIMESTAMP
+
+/* Default environment */
+#define CONFIG_ENV_CONSOLEDEV					\
+	"consoledev=" CONFIG_CONSOLE_DEV "\0"
+
+#define CONFIG_ENV_ADDCONSOLE					\
+	"addconsole=setenv bootargs $bootargs"			\
+	" console=$consoledev,$baudrate\0"
+
+#if defined(CONFIG_NET_DEV)
+#define CONFIG_ENV_NETDEV					\
+	"netdev=" CONFIG_NET_DEV "\0"
+#else
+#define CONFIG_ENV_NETDEV					\
+	"netdev=eth0\0"
+#endif
+
+#define CONFIG_ENV_ADDIP					\
+	"addip=setenv bootargs $bootargs"			\
+	" ip=$ipaddr:$serverip::::$netdev:off\0"
+
+#define CONFIG_ENV_ADDETH					\
+	"addeth=setenv bootargs $bootargs"			\
+	" ethaddr=$ethaddr\0"
+
+#define CONFIG_ENV_ADDMACHTYPE					\
+	"addmachtype=setenv bootargs $bootargs"			\
+	" machtype=" CONFIG_MACH_TYPE "\0"
+
+#if defined(CONFIG_LTQ_SUPPORT_NOR_FLASH)
+#define CONFIG_ENV_WRITE_UBOOT_NOR					\
+	"write-uboot-nor="						\
+	"protect off " __stringify(CONFIG_SYS_FLASH_BASE) " +$filesize && " \
+	"erase " __stringify(CONFIG_SYS_FLASH_BASE) " +$filesize && "	\
+	"cp.b $fileaddr " __stringify(CONFIG_SYS_FLASH_BASE) " $filesize\0"
+
+#define CONFIG_ENV_LOAD_UBOOT_NOR					\
+	"load-uboot-nor=tftpboot u-boot.bin\0"				\
+	"load-uboot-norspl=tftpboot u-boot.ltq.norspl\0"		\
+	"load-uboot-norspl-lzo=tftpboot u-boot.ltq.lzo.norspl\0"	\
+	"load-uboot-norspl-lzma=tftpboot u-boot.ltq.lzma.norspl\0"
+#else
+#define CONFIG_ENV_WRITE_UBOOT_NOR
+#define CONFIG_ENV_LOAD_UBOOT_NOR
+#endif
+
+#if defined(CONFIG_LTQ_SUPPORT_SPI_FLASH)
+#define CONFIG_ENV_SF_PROBE					\
+	"sf-probe=sf probe " __stringify(CONFIG_ENV_SPI_CS) " "	\
+	__stringify(CONFIG_ENV_SPI_MAX_HZ) " "			\
+	__stringify(CONFIG_ENV_SPI_MODE) " \0"
+
+#define CONFIG_ENV_WRITE_UBOOT_SF				\
+	"write-uboot-sf="					\
+	"run sf-probe && sf erase 0 +$filesize && "		\
+	"sf write $fileaddr 0 $filesize\0"
+
+#define CONFIG_ENV_LOAD_UBOOT_SF					\
+	"load-uboot-sfspl=tftpboot u-boot.ltq.sfspl\0"			\
+	"load-uboot-sfspl-lzo=tftpboot u-boot.ltq.lzo.sfspl\0"		\
+	"load-uboot-sfspl-lzma=tftpboot u-boot.ltq.lzma.sfspl\0"
+#else
+#define CONFIG_ENV_SF_PROBE
+#define CONFIG_ENV_WRITE_UBOOT_SF
+#define CONFIG_ENV_LOAD_UBOOT_SF
+#endif
+
+#define CONFIG_ENV_LANTIQ_DEFAULTS	\
+	CONFIG_ENV_CONSOLEDEV		\
+	CONFIG_ENV_ADDCONSOLE		\
+	CONFIG_ENV_NETDEV		\
+	CONFIG_ENV_ADDIP		\
+	CONFIG_ENV_ADDETH		\
+	CONFIG_ENV_ADDMACHTYPE		\
+	CONFIG_ENV_WRITE_UBOOT_NOR	\
+	CONFIG_ENV_LOAD_UBOOT_NOR	\
+	CONFIG_ENV_SF_PROBE		\
+	CONFIG_ENV_WRITE_UBOOT_SF	\
+	CONFIG_ENV_LOAD_UBOOT_SF
+
+#endif /* __LANTIQ_CONFIG_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/lantiq/cpu.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LANTIQ_CPU_H__
+#define __LANTIQ_CPU_H__
+
+enum ltq_boot_select {
+	BOOT_NOR,
+	BOOT_NOR_NO_BOOTROM,
+	BOOT_UART,
+	BOOT_UART_NO_EEPROM,
+	BOOT_SPI,
+	BOOT_NAND,
+	BOOT_PCI,
+	BOOT_MII0,
+	BOOT_RMII0,
+	BOOT_RGMII1,
+	BOOT_UNKNOWN,
+};
+
+enum ltq_boot_select ltq_boot_select(void);
+const char *ltq_boot_select_str(void);
+
+void ltq_pmu_init(void);
+void ltq_ebu_init(void);
+void ltq_gpio_init(void);
+
+void ltq_pll_init(void);
+void ltq_dcdc_init(unsigned int dig_ref);
+
+#endif /* __LANTIQ_CPU_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/lantiq/dma.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LANTIQ_DMA_H__
+#define __LANTIQ_DMA_H__
+
+enum ltq_dma_endianess {
+	LTQ_DMA_ENDIANESS_B0_B1_B2_B3,	/* No byte swapping */
+	LTQ_DMA_ENDIANESS_B1_B0_B3_B2,	/* B0B1B2B3 => B1B0B3B2 */
+	LTQ_DMA_ENDIANESS_B2_B3_B0_B1,	/* B0B1B2B3 => B2B3B0B1 */
+	LTQ_DMA_ENDIANESS_B3_B2_B1_B0,	/* B0B1B2B3 => B3B2B1B0 */
+};
+
+enum ltq_dma_burst_len {
+	LTQ_DMA_BURST_2WORDS = 1,
+	LTQ_DMA_BURST_4WORDS = 2,
+	LTQ_DMA_BURST_8WORDS = 3,
+};
+
+struct ltq_dma_desc {
+	u32 ctl;
+	u32 addr;
+};
+
+struct ltq_dma_channel {
+	struct ltq_dma_device *dev;
+	u8 chan_no;
+	u8 class;
+	u16 num_desc;
+	struct ltq_dma_desc *desc_base;
+	void *mem_base;
+	u32 dma_addr;
+};
+
+struct ltq_dma_device {
+	enum ltq_dma_endianess rx_endian_swap;
+	enum ltq_dma_endianess tx_endian_swap;
+	enum ltq_dma_burst_len rx_burst_len;
+	enum ltq_dma_burst_len tx_burst_len;
+	struct ltq_dma_channel rx_chan;
+	struct ltq_dma_channel tx_chan;
+	u8 port;
+};
+
+/**
+ * Initialize DMA hardware and driver
+ */
+void ltq_dma_init(void);
+
+/**
+ * Register given DMA client context
+ *
+ * @returns 0 on success, negative value otherwise
+ */
+int ltq_dma_register(struct ltq_dma_device *dev);
+
+/**
+ * Reset and halt all channels related to given DMA client
+ */
+void ltq_dma_reset(struct ltq_dma_device *dev);
+void ltq_dma_enable(struct ltq_dma_device *dev);
+void ltq_dma_disable(struct ltq_dma_device *dev);
+
+/**
+ * Map RX DMA descriptor to memory region
+ *
+ * @returns 0 on success, negative value otherwise
+ */
+int ltq_dma_rx_map(struct ltq_dma_device *dev, int index, void *data, int len);
+
+/**
+ * Check if new data is available.
+ *
+ * @returns length of received data, 0 otherwise
+ */
+int ltq_dma_rx_poll(struct ltq_dma_device *dev, int index);
+
+int ltq_dma_rx_length(struct ltq_dma_device *dev, int index);
+
+/**
+ * Map TX DMA descriptor to memory region
+ *
+ * @returns 0 on success, negative value otherwise
+ */
+int ltq_dma_tx_map(struct ltq_dma_device *dev, int index, void *data, int len,
+			unsigned long timeout);
+
+int ltq_dma_tx_wait(struct ltq_dma_device *dev, int index,
+			unsigned long timeout);
+
+#endif /* __LANTIQ_DMA_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/lantiq/eth.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LANTIQ_ETH_H__
+#define __LANTIQ_ETH_H__
+
+#include <phy.h>
+
+enum LTQ_ETH_PORT_FLAGS {
+	LTQ_ETH_PORT_NONE	= 0,
+	LTQ_ETH_PORT_PHY	= 1,
+	LTQ_ETH_PORT_SWITCH	= (1 << 1),
+	LTQ_ETH_PORT_MAC	= (1 << 2),
+};
+
+struct ltq_eth_port_config {
+	u8 num;
+	u8 phy_addr;
+	u16 flags;
+	phy_interface_t phy_if;
+	u8 rgmii_rx_delay;
+	u8 rgmii_tx_delay;
+};
+
+struct ltq_eth_board_config {
+	const struct ltq_eth_port_config *ports;
+	int num_ports;
+};
+
+extern int ltq_eth_initialize(const struct ltq_eth_board_config *board_config);
+
+#endif /* __LANTIQ_ETH_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/lantiq/gpio.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LANTIQ_GPIO_H__
+#define __LANTIQ_GPIO_H__
+
+enum ltq_gpio_dir {
+	GPIO_DIR_IN = 0,
+	GPIO_DIR_OUT
+};
+
+enum ltq_gpio_od {
+	GPIO_OD_ACTIVE = 0,
+	GPIO_OD_NORMAL
+};
+
+enum ltq_gpio_altsel {
+	GPIO_ALTSEL_CLR = 0,
+	GPIO_ALTSEL_SET
+};
+
+extern int gpio_set_altfunc(unsigned gpio, int altsel0, int altsel1, int dir);
+extern int gpio_set_opendrain(unsigned gpio, int od);
+
+static inline int gpio_to_port(unsigned gpio)
+{
+	return gpio >> 4;
+}
+
+static inline int gpio_to_pin(unsigned gpio)
+{
+	return gpio & 0xF;
+}
+
+static inline int gpio_to_bit(unsigned gpio)
+{
+	return 1 << gpio_to_pin(gpio);
+}
+
+static inline int gpio_to_gpio(unsigned port, unsigned pin)
+{
+	return (port << 4) | (pin & 0xF);
+}
+
+#include <asm-generic/gpio.h>
+
+#endif /* __LANTIQ_GPIO_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/lantiq/io.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LANTIQ_IO_H__
+#define __LANTIQ_IO_H__
+
+#include <asm/io.h>
+
+#define ltq_readb(a)		__raw_readb(a)
+#define ltq_writeb(a, v)	__raw_writeb(v, a)
+
+#define ltq_readl(a)		__raw_readl(a)
+#define ltq_writel(a, v)	__raw_writel(v, a)
+
+#define ltq_clrbits(a, clear) \
+	ltq_writel(a, ltq_readl(a) & ~(clear))
+
+#define ltq_setbits(a, set) \
+	ltq_writel(a, ltq_readl(a) | (set))
+
+#define ltq_clrsetbits(a, clear, set) \
+	ltq_writel(a, (ltq_readl(a) & ~(clear)) | (set))
+
+static inline void ltq_reg_dump(const void *addr, const char *desc)
+{
+	u32 data;
+
+	data = ltq_readl(addr);
+	printf("ltq_reg_dump: %s 0x%p = 0x%08x\n",
+		desc, addr, data);
+}
+
+#endif /* __LANTIQ_IO_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/lantiq/pm.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LANTIQ_PM_H__
+#define __LANTIQ_PM_H__
+
+enum ltq_pm_modules {
+	LTQ_PM_CORE,
+	LTQ_PM_DMA,
+	LTQ_PM_ETH,
+	LTQ_PM_SPI,
+};
+
+u32 ltq_pm_map(enum ltq_pm_modules module);
+int ltq_pm_enable(enum ltq_pm_modules module);
+int ltq_pm_disable(enum ltq_pm_modules module);
+
+#endif /* __LANTIQ_PM_H__ */
--- /dev/null
+++ b/arch/mips/include/asm/lantiq/reset.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __LANTIQ_RESET_H__
+#define __LANTIQ_RESET_H__
+
+enum ltq_reset_modules {
+	LTQ_RESET_CORE,
+	LTQ_RESET_DMA,
+	LTQ_RESET_ETH,
+	LTQ_RESET_PHY,
+	LTQ_RESET_HARD,
+	LTQ_RESET_SOFT,
+};
+
+extern u32 ltq_reset_map(enum ltq_reset_modules module);
+extern int ltq_reset_activate(enum ltq_reset_modules module);
+extern int ltq_reset_deactivate(enum ltq_reset_modules module);
+
+static inline int ltq_reset_once(enum ltq_reset_modules module, ulong usec)
+{
+	int ret;
+
+	ret = ltq_reset_activate(module);
+	if (ret)
+		return ret;
+
+	__udelay(usec);
+	ret = ltq_reset_deactivate(module);
+
+	return ret;
+}
+
+#endif /* __LANTIQ_RESET_H__ */
--- a/arch/mips/include/asm/mipsregs.h
+++ b/arch/mips/include/asm/mipsregs.h
@@ -46,7 +46,10 @@
 #define CP0_ENTRYLO1 $3
 #define CP0_CONF $3
 #define CP0_CONTEXT $4
+#define CP0_CONTEXTCONFIG $4,1
+#define CP0_USERLOCAL $4,1
 #define CP0_PAGEMASK $5
+#define CP0_PAGEGRAIN $5,1
 #define CP0_WIRED $6
 #define CP0_INFO $7
 #define CP0_BADVADDR $8
@@ -54,10 +57,19 @@
 #define CP0_ENTRYHI $10
 #define CP0_COMPARE $11
 #define CP0_STATUS $12
+#define CP0_INTCTL $12,1
+#define CP0_SRSCTL $12,2
+#define CP0_SRSMAP $12,3
+#define CP0_SRSHIGH $12,4
 #define CP0_CAUSE $13
 #define CP0_EPC $14
 #define CP0_PRID $15
+#define CP0_EBASE $15,1
 #define CP0_CONFIG $16
+#define CP0_CONFIG1 $16,1
+#define CP0_CONFIG2 $16,2
+#define CP0_CONFIG3 $16,3
+#define CP0_CONFIG7 $16,7
 #define CP0_LLADDR $17
 #define CP0_WATCHLO $18
 #define CP0_WATCHHI $19
@@ -70,7 +82,17 @@
 #define CP0_ECC $26
 #define CP0_CACHEERR $27
 #define CP0_TAGLO $28
+#define CP0_ITAGLO $28
+#define CP0_IDATALO $28,1
+#define CP0_DTAGLO $28,2
+#define CP0_DDATALO $28,3
+#define CP0_L23TAGLO $28,4
+#define CP0_L23DATALO $28,5
 #define CP0_TAGHI $29
+#define CP0_IDATAHI $29,1
+#define CP0_DTAGHI $29,2
+#define CP0_L23TAGHI $29,4
+#define CP0_L23DATAHI $29,5
 #define CP0_ERROREPC $30
 #define CP0_DESAVE $31
 
@@ -395,6 +417,12 @@
 #define  CAUSEF_BD		(_ULCAST_(1)   << 31)
 
 /*
+ * Bits in the coprocessor 0 EBase register.
+ */
+#define EBASEB_CPUNUM		0
+#define EBASEF_CPUNUM		(_ULCAST_(1023))
+
+/*
  * Bits in the coprocessor 0 config register.
  */
 /* Generic bits.  */
--- a/arch/mips/include/asm/u-boot-mips.h
+++ b/arch/mips/include/asm/u-boot-mips.h
@@ -23,3 +23,4 @@ static inline unsigned long image_copy_e
 }
 
 extern int incaip_set_cpuclk(void);
+extern int arch_cpu_init(void);
--- a/arch/mips/lib/board.c
+++ b/arch/mips/lib/board.c
@@ -33,6 +33,16 @@ static char *failed = "*** failed ***\n"
  */
 const unsigned long mips_io_port_base = -1;
 
+int __arch_cpu_init(void)
+{
+	/*
+	 * Nothing to do in this dummy implementation
+	 */
+	return 0;
+}
+int arch_cpu_init(void)
+	__attribute__((weak, alias("__arch_cpu_init")));
+
 int __board_early_init_f(void)
 {
 	/*
@@ -106,6 +116,7 @@ static int init_baudrate(void)
 typedef int (init_fnc_t)(void);
 
 init_fnc_t *init_sequence[] = {
+	arch_cpu_init,
 	board_early_init_f,
 	timer_init,
 	env_init,		/* initialize environment */
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -12,6 +12,7 @@ LIB	:= $(obj)libdma.o
 COBJS-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o
 COBJS-$(CONFIG_APBH_DMA) += apbh_dma.o
 COBJS-$(CONFIG_FSL_DMA) += fsl_dma.o
+COBJS-$(CONFIG_LANTIQ_DMA) += lantiq_dma.o
 COBJS-$(CONFIG_OMAP3_DMA) += omap3_dma.o
 
 COBJS	:= $(COBJS-y)
--- /dev/null
+++ b/drivers/dma/lantiq_dma.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <watchdog.h>
+#include <linux/compiler.h>
+#include <asm/lantiq/io.h>
+#include <asm/lantiq/dma.h>
+#include <asm/lantiq/pm.h>
+#include <asm/lantiq/reset.h>
+#include <asm/arch/soc.h>
+#include <asm/processor.h>
+
+#define DMA_CTRL_PKTARB			(1 << 31)
+#define DMA_CTRL_MBRSTARB		(1 << 30)
+#define DMA_CTRL_MBRSTCNT_SHIFT		16
+#define DMA_CTRL_MBRSTCNT_MASK		(0x3ff << DMA_CTRL_MBRSTCNT_SHIFT)
+#define DMA_CTRL_DRB			(1 << 8)
+#define DMA_CTRL_RESET			(1 << 0)
+
+#define DMA_CPOLL_EN			(1 << 31)
+#define DMA_CPOLL_CNT_SHIFT		4
+#define DMA_CPOLL_CNT_MASK		(0xFFF << DMA_CPOLL_CNT_SHIFT)
+
+#define DMA_CCTRL_TXWGT_SHIFT		16
+#define DMA_CCTRL_TXWGT_MASK		(0x3 << DMA_CCTRL_TXWGT_SHIFT)
+#define DMA_CCTRL_CLASS_SHIFT		9
+#define DMA_CCTRL_CLASS_MASK		(0x3 << DMA_CCTRL_CLASS_SHIFT)
+#define DMA_CCTRL_RST			(1 << 1)
+#define DMA_CCTRL_ONOFF			(1 << 0)
+
+#define DMA_PCTRL_TXBL_SHIFT		4
+#define DMA_PCTRL_TXBL_2WORDS		(1 << DMA_PCTRL_TXBL_SHIFT)
+#define DMA_PCTRL_TXBL_4WORDS		(2 << DMA_PCTRL_TXBL_SHIFT)
+#define DMA_PCTRL_TXBL_8WORDS		(3 << DMA_PCTRL_TXBL_SHIFT)
+#define DMA_PCTRL_RXBL_SHIFT		2
+#define DMA_PCTRL_RXBL_2WORDS		(1 << DMA_PCTRL_RXBL_SHIFT)
+#define DMA_PCTRL_RXBL_4WORDS		(2 << DMA_PCTRL_RXBL_SHIFT)
+#define DMA_PCTRL_RXBL_8WORDS		(3 << DMA_PCTRL_RXBL_SHIFT)
+#define DMA_PCTRL_TXENDI_SHIFT		10
+#define DMA_PCTRL_TXENDI_MASK		(0x3 << DMA_PCTRL_TXENDI_SHIFT)
+#define DMA_PCTRL_RXENDI_SHIFT		8
+#define DMA_PCTRL_RXENDI_MASK		(0x3 << DMA_PCTRL_RXENDI_SHIFT)
+
+#define DMA_DESC_OWN			(1 << 31)
+#define DMA_DESC_C			(1 << 30)
+#define DMA_DESC_SOP			(1 << 29)
+#define DMA_DESC_EOP			(1 << 28)
+#define DMA_DESC_TX_OFFSET(x)		((x & 0x1f) << 23)
+#define DMA_DESC_RX_OFFSET(x)		((x & 0x3) << 23)
+#define DMA_DESC_LENGTH(x)		(x & 0xffff)
+
+#define PTR_ALIGN(p, a)		((typeof(p))ALIGN((unsigned long)(p), (a)))
+
+struct ltq_dma_regs {
+	u32	clc;		/* Clock control */
+	u32	rsvd0;
+	u32	id;		/* Identification */
+	u32	rsvd1;
+	u32	ctrl;		/* Control */
+	u32	cpoll;		/* Channel polling */
+	u32	cs;		/* Channel select */
+	u32	cctrl;		/* Channel control */
+	u32	cdba;		/* Channel descriptor base address */
+	u32	cdlen;		/* Channel descriptor length */
+	u32	cis;		/* Channel interrupt status */
+	u32	cie;		/* Channel interrupt enable */
+	u32	cgbl;		/* Channel global buffer length */
+	u32	cdptnrd;	/* Current descriptor pointer */
+	u32	rsvd2[2];
+	u32	ps;		/* Port select */
+	u32	pctrl;		/* Port control */
+	u32	rsvd3[43];
+	u32	irnen;		/* Interrupt node enable */
+	u32	irncr;		/* Interrupt node control */
+	u32	irnicr;		/* Interrupt capture */
+};
+
+static struct ltq_dma_regs *ltq_dma_regs =
+	(struct ltq_dma_regs *) CKSEG1ADDR(LTQ_DMA_BASE);
+
+static inline unsigned long ltq_dma_addr_to_virt(u32 dma_addr)
+{
+	return KSEG0ADDR(dma_addr);
+}
+
+static inline u32 ltq_virt_to_dma_addr(void *addr)
+{
+	return CPHYSADDR(addr);
+}
+
+static inline int ltq_dma_burst_align(enum ltq_dma_burst_len burst_len)
+{
+	switch (burst_len) {
+	case LTQ_DMA_BURST_2WORDS:
+		return 2 * 4;
+	case LTQ_DMA_BURST_4WORDS:
+		return 4 * 4;
+	case LTQ_DMA_BURST_8WORDS:
+		return 8 * 4;
+	}
+
+	return 0;
+}
+
+static inline void ltq_dma_sync(void)
+{
+	__asm__ __volatile__("sync");
+}
+
+static inline void ltq_dma_dcache_wb_inv(const void *ptr, size_t size)
+{
+	unsigned long addr = (unsigned long) ptr;
+
+	flush_dcache_range(addr, addr + size);
+	ltq_dma_sync();
+}
+
+static inline void ltq_dma_dcache_inv(const void *ptr, size_t size)
+{
+	unsigned long addr = (unsigned long) ptr;
+
+	invalidate_dcache_range(addr, addr + size);
+}
+
+void ltq_dma_init(void)
+{
+	/* Power up DMA */
+	ltq_pm_enable(LTQ_PM_DMA);
+
+	/* Reset DMA */
+	ltq_setbits(&ltq_dma_regs->ctrl, DMA_CTRL_RESET);
+
+	/* Disable and clear all interrupts */
+	ltq_writel(&ltq_dma_regs->irnen, 0);
+	ltq_writel(&ltq_dma_regs->irncr, 0xFFFFF);
+
+#if 0
+	/* Enable packet arbitration */
+	ltq_setbits(&ltq_dma_regs->ctrl, DMA_CTRL_PKTARB);
+#endif
+
+#if 0
+	/* Enable descriptor read back */
+	ltq_setbits(&ltq_dma_regs->ctrl, DMA_CTRL_DRB);
+#endif
+
+	/* Enable polling for descriptor fetching for all channels */
+	ltq_writel(&ltq_dma_regs->cpoll, DMA_CPOLL_EN |
+		(4 << DMA_CPOLL_CNT_SHIFT));
+}
+
+static void ltq_dma_channel_reset(struct ltq_dma_channel *chan)
+{
+	ltq_writel(&ltq_dma_regs->cs, chan->chan_no);
+	ltq_setbits(&ltq_dma_regs->cctrl, DMA_CCTRL_RST);
+}
+
+static void ltq_dma_channel_enable(struct ltq_dma_channel *chan)
+{
+	ltq_writel(&ltq_dma_regs->cs, chan->chan_no);
+	ltq_setbits(&ltq_dma_regs->cctrl, DMA_CCTRL_ONOFF);
+}
+
+static void ltq_dma_channel_disable(struct ltq_dma_channel *chan)
+{
+	ltq_writel(&ltq_dma_regs->cs, chan->chan_no);
+	ltq_clrbits(&ltq_dma_regs->cctrl, DMA_CCTRL_ONOFF);
+}
+
+static void ltq_dma_port_init(struct ltq_dma_device *dev)
+{
+	u32 pctrl;
+
+	pctrl = dev->tx_endian_swap << DMA_PCTRL_TXENDI_SHIFT;
+	pctrl |= dev->rx_endian_swap << DMA_PCTRL_RXENDI_SHIFT;
+	pctrl |= dev->tx_burst_len << DMA_PCTRL_TXBL_SHIFT;
+	pctrl |= dev->rx_burst_len << DMA_PCTRL_RXBL_SHIFT;
+
+	ltq_writel(&ltq_dma_regs->ps, dev->port);
+	ltq_writel(&ltq_dma_regs->pctrl, pctrl);
+}
+
+static int ltq_dma_alloc_descriptors(struct ltq_dma_device *dev,
+					struct ltq_dma_channel *chan)
+{
+	size_t size;
+	void *desc_base;
+
+	size = ALIGN(sizeof(struct ltq_dma_desc) * chan->num_desc +
+			ARCH_DMA_MINALIGN, ARCH_DMA_MINALIGN);
+
+	chan->mem_base = malloc(size);
+	if (!chan->mem_base)
+		return 1;
+
+	memset(chan->mem_base, 0, size);
+	ltq_dma_dcache_wb_inv(chan->mem_base, size);
+
+	desc_base = PTR_ALIGN(chan->mem_base, ARCH_DMA_MINALIGN);
+
+	debug("DMA: mem %p, desc %p\n", chan->mem_base, desc_base);
+
+	/* Align descriptor base to 8 bytes */
+	chan->desc_base = (void *) CKSEG1ADDR(desc_base);
+	chan->dma_addr = CPHYSADDR(desc_base);
+	chan->dev = dev;
+
+	debug("DMA: desc_base %p, size %u\n", chan->desc_base, size);
+
+	/* Configure hardware with location of descriptor list */
+	ltq_writel(&ltq_dma_regs->cs, chan->chan_no);
+	ltq_writel(&ltq_dma_regs->cdba, chan->dma_addr);
+	ltq_writel(&ltq_dma_regs->cdlen, chan->num_desc);
+	ltq_writel(&ltq_dma_regs->cctrl, (3 << DMA_CCTRL_TXWGT_SHIFT) |
+		(chan->class << DMA_CCTRL_CLASS_SHIFT));
+	ltq_writel(&ltq_dma_regs->cctrl, DMA_CCTRL_RST);
+
+	return 0;
+}
+
+static void ltq_dma_free_descriptors(struct ltq_dma_channel *chan)
+{
+	ltq_writel(&ltq_dma_regs->cs, chan->chan_no);
+	ltq_writel(&ltq_dma_regs->cdba, 0);
+	ltq_writel(&ltq_dma_regs->cdlen, 0);
+
+	ltq_dma_channel_reset(chan);
+
+	free(chan->mem_base);
+}
+
+int ltq_dma_register(struct ltq_dma_device *dev)
+{
+	int ret;
+
+	ltq_dma_port_init(dev);
+
+	ret = ltq_dma_alloc_descriptors(dev, &dev->rx_chan);
+	if (ret)
+		return ret;
+
+	ret = ltq_dma_alloc_descriptors(dev, &dev->tx_chan);
+	if (ret) {
+		ltq_dma_free_descriptors(&dev->rx_chan);
+		return ret;
+	}
+
+	return 0;
+}
+
+void ltq_dma_reset(struct ltq_dma_device *dev)
+{
+	ltq_dma_channel_reset(&dev->rx_chan);
+	ltq_dma_channel_reset(&dev->tx_chan);
+}
+
+void ltq_dma_enable(struct ltq_dma_device *dev)
+{
+	ltq_dma_channel_enable(&dev->rx_chan);
+	ltq_dma_channel_enable(&dev->tx_chan);
+}
+
+void ltq_dma_disable(struct ltq_dma_device *dev)
+{
+	ltq_dma_channel_disable(&dev->rx_chan);
+	ltq_dma_channel_disable(&dev->tx_chan);
+}
+
+int ltq_dma_rx_map(struct ltq_dma_device *dev, int index, void *data, int len)
+{
+	struct ltq_dma_channel *chan = &dev->rx_chan;
+	struct ltq_dma_desc *desc = &chan->desc_base[index];
+	u32 dma_addr = ltq_virt_to_dma_addr(data);
+	unsigned int offset;
+
+	offset = dma_addr % ltq_dma_burst_align(dev->rx_burst_len);
+
+	ltq_dma_dcache_inv(data, len);
+
+#if 0
+	printf("%s: index %d, data %p, dma_addr %08x, offset %u, len %d\n",
+		__func__, index, data, dma_addr, offset, len);
+#endif
+
+
+	desc->addr = dma_addr - offset;
+	desc->ctl = DMA_DESC_OWN | DMA_DESC_RX_OFFSET(offset) |
+			DMA_DESC_LENGTH(len);
+
+#if 0
+	printf("%s: index %d, desc %p, desc->ctl %08x\n",
+		__func__, index, desc, desc->ctl);
+#endif
+
+	return 0;
+}
+
+int ltq_dma_rx_poll(struct ltq_dma_device *dev, int index)
+{
+	struct ltq_dma_channel *chan = &dev->rx_chan;
+	struct ltq_dma_desc *desc = &chan->desc_base[index];
+
+#if 0
+	printf("%s: index %d, desc %p, desc->ctl %08x\n",
+		__func__, index, desc, desc->ctl);
+#endif
+
+	if (desc->ctl & DMA_DESC_OWN)
+		return 0;
+
+	if (desc->ctl & DMA_DESC_C)
+		return 1;
+
+	return 0;
+}
+
+int ltq_dma_rx_length(struct ltq_dma_device *dev, int index)
+{
+	struct ltq_dma_channel *chan = &dev->rx_chan;
+	struct ltq_dma_desc *desc = &chan->desc_base[index];
+
+	return DMA_DESC_LENGTH(desc->ctl);
+}
+
+int ltq_dma_tx_map(struct ltq_dma_device *dev, int index, void *data, int len,
+			unsigned long timeout)
+{
+	struct ltq_dma_channel *chan = &dev->tx_chan;
+	struct ltq_dma_desc *desc = &chan->desc_base[index];
+	unsigned int offset;
+	unsigned long timebase = get_timer(0);
+	u32 dma_addr = ltq_virt_to_dma_addr(data);
+
+	while (desc->ctl & DMA_DESC_OWN) {
+		WATCHDOG_RESET();
+
+		if (get_timer(timebase) >= timeout) {
+#if 0
+			printf("%s: timeout: index %d, desc %p, desc->ctl %08x\n",
+				__func__, index, desc, desc->ctl);
+#endif
+			return -1;
+		}
+	}
+
+	offset = dma_addr % ltq_dma_burst_align(dev->rx_burst_len);
+
+#if 0
+	printf("%s: index %d, desc %p, data %p, dma_addr %08x, offset %u, len %d\n",
+		__func__, index, desc, data, dma_addr, offset, len);
+#endif
+
+	ltq_dma_dcache_wb_inv(data, len);
+
+	desc->addr = dma_addr - offset;
+	desc->ctl = DMA_DESC_OWN | DMA_DESC_SOP | DMA_DESC_EOP |
+			DMA_DESC_TX_OFFSET(offset) | DMA_DESC_LENGTH(len);
+
+#if 0
+	printf("%s: index %d, desc %p, desc->ctl %08x\n",
+		__func__, index, desc, desc->ctl);
+#endif
+
+	return 0;
+}
+
+int ltq_dma_tx_wait(struct ltq_dma_device *dev, int index,
+			unsigned long timeout)
+{
+	struct ltq_dma_channel *chan = &dev->tx_chan;
+	struct ltq_dma_desc *desc = &chan->desc_base[index];
+	unsigned long timebase = get_timer(0);
+
+	while ((desc->ctl & (DMA_DESC_OWN | DMA_DESC_C)) != DMA_DESC_C) {
+		WATCHDOG_RESET();
+
+		if (get_timer(timebase) >= timeout)
+			return -1;
+	}
+
+	return 0;
+}
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -12,6 +12,7 @@ LIB 	:= $(obj)libgpio.o
 COBJS-$(CONFIG_AT91_GPIO)	+= at91_gpio.o
 COBJS-$(CONFIG_INTEL_ICH6_GPIO)	+= intel_ich6_gpio.o
 COBJS-$(CONFIG_KIRKWOOD_GPIO)	+= kw_gpio.o
+COBJS-$(CONFIG_LANTIQ_GPIO)	+= lantiq_gpio.o
 COBJS-$(CONFIG_MARVELL_GPIO)	+= mvgpio.o
 COBJS-$(CONFIG_MARVELL_MFP)	+= mvmfp.o
 COBJS-$(CONFIG_MXC_GPIO)	+= mxc_gpio.o
--- /dev/null
+++ b/drivers/gpio/lantiq_gpio.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/soc.h>
+#include <asm/arch/gpio.h>
+#include <asm/lantiq/io.h>
+
+#define SSIO_GPIO_BASE		64
+
+#define SSIO_CON0_SWU		(1 << 31)
+#define SSIO_CON0_RZFL		(1 << 26)
+#define SSIO_CON0_GPHY1_SHIFT	27
+#define SSIO_CON0_GPHY1_CONFIG	((CONFIG_LTQ_SSIO_GPHY1_MODE & 0x7) << 27)
+
+#define SSIO_CON1_US_FPI	(2 << 30)
+#define SSIO_CON1_FPID_2HZ	(0 << 23)
+#define SSIO_CON1_FPID_4HZ	(1 << 23)
+#define SSIO_CON1_FPID_8HZ	(2 << 23)
+#define SSIO_CON1_FPID_10HZ	(3 << 23)
+#define SSIO_CON1_FPIS_1_2	(1 << 20)
+#define SSIO_CON1_FPIS_1_32	(2 << 20)
+#define SSIO_CON1_FPIS_1_64	(3 << 20)
+
+#define SSIO_CON1_GPHY2_SHIFT	15
+#define SSIO_CON1_GPHY2_CONFIG	((CONFIG_LTQ_SSIO_GPHY2_MODE & 0x7) << 15)
+
+#define SSIO_CON1_GROUP2	(1 << 2)
+#define SSIO_CON1_GROUP1	(1 << 1)
+#define SSIO_CON1_GROUP0	(1 << 0)
+#define SSIO_CON1_GROUP_CONFIG	(0x3)
+
+#ifdef CONFIG_LTQ_SSIO_SHIFT_REGS
+#define enable_ssio	1
+#else
+#define enable_ssio	0
+
+#define CONFIG_LTQ_SSIO_GPHY1_MODE	0
+#define CONFIG_LTQ_SSIO_GPHY2_MODE	0
+#define CONFIG_LTQ_SSIO_INIT_VALUE	0
+#endif
+
+#ifdef CONFIG_LTQ_SSIO_EDGE_FALLING
+#define SSIO_RZFL_CONFIG	SSIO_CON0_RZFL
+#else
+#define SSIO_RZFL_CONFIG	0
+#endif
+
+struct ltq_gpio_port_regs {
+	__be32	out;
+	__be32	in;
+	__be32	dir;
+	__be32	altsel0;
+	__be32	altsel1;
+	__be32	od;
+	__be32	stoff;
+	__be32	pudsel;
+	__be32	puden;
+	__be32	rsvd1[3];
+};
+
+struct ltq_gpio_regs {
+	u32				rsvd[4];
+	struct ltq_gpio_port_regs	ports[CONFIG_LTQ_GPIO_MAX_BANKS];
+};
+
+struct ltq_gpio3_regs {
+	u32	rsvd0[13];
+	__be32	od;
+	__be32	pudsel;
+	__be32	puden;
+	u32	rsvd1[9];
+	__be32	altsel1;
+	u32	rsvd2[14];
+	__be32	out;
+	__be32	in;
+	__be32	dir;
+	__be32	altsel0;
+};
+
+struct ltq_ssio_regs {
+	__be32	con0;
+	__be32	con1;
+	__be32	cpu0;
+	__be32	cpu1;
+	__be32	ar;
+};
+
+static struct ltq_gpio_regs *ltq_gpio_regs =
+	(struct ltq_gpio_regs *) CKSEG1ADDR(LTQ_GPIO_BASE);
+
+static struct ltq_gpio3_regs *ltq_gpio3_regs =
+	(struct ltq_gpio3_regs *) CKSEG1ADDR(LTQ_GPIO_BASE);
+
+static struct ltq_ssio_regs *ltq_ssio_regs =
+	(struct ltq_ssio_regs *) CKSEG1ADDR(LTQ_SSIO_BASE);
+
+static int is_gpio_bank3(unsigned int port)
+{
+#ifdef CONFIG_LTQ_HAS_GPIO_BANK3
+	return port == 3;
+#else
+	return 0;
+#endif
+}
+
+static int is_gpio_ssio(unsigned int gpio)
+{
+#ifdef CONFIG_LTQ_SSIO_SHIFT_REGS
+	return gpio >= SSIO_GPIO_BASE;
+#else
+	return 0;
+#endif
+}
+
+static inline int ssio_gpio_to_bit(unsigned gpio)
+{
+	return 1 << (gpio - SSIO_GPIO_BASE);
+}
+
+int ltq_gpio_init(void)
+{
+	ltq_writel(&ltq_ssio_regs->ar, 0);
+	ltq_writel(&ltq_ssio_regs->cpu0, CONFIG_LTQ_SSIO_INIT_VALUE);
+	ltq_writel(&ltq_ssio_regs->cpu1, 0);
+	ltq_writel(&ltq_ssio_regs->con0, SSIO_CON0_SWU);
+
+	if (enable_ssio) {
+		ltq_writel(&ltq_ssio_regs->con0, SSIO_CON0_GPHY1_CONFIG |
+			SSIO_RZFL_CONFIG);
+		ltq_writel(&ltq_ssio_regs->con1, SSIO_CON1_US_FPI |
+			SSIO_CON1_FPID_8HZ | SSIO_CON1_GPHY2_CONFIG |
+			SSIO_CON1_GROUP_CONFIG);
+	}
+
+	return 0;
+}
+
+int gpio_request(unsigned gpio, const char *label)
+{
+	return 0;
+}
+
+int gpio_free(unsigned gpio)
+{
+	return 0;
+}
+
+int gpio_direction_input(unsigned gpio)
+{
+	unsigned port = gpio_to_port(gpio);
+	const void *gpio_od = &ltq_gpio_regs->ports[port].od;
+	const void *gpio_altsel0 = &ltq_gpio_regs->ports[port].altsel0;
+	const void *gpio_altsel1 = &ltq_gpio_regs->ports[port].altsel1;
+	const void *gpio_dir = &ltq_gpio_regs->ports[port].dir;
+
+	if (is_gpio_ssio(gpio))
+		return 0;
+
+	if (is_gpio_bank3(port)) {
+		gpio_od = &ltq_gpio3_regs->od;
+		gpio_altsel0 = &ltq_gpio3_regs->altsel0;
+		gpio_altsel1 = &ltq_gpio3_regs->altsel1;
+		gpio_dir = &ltq_gpio3_regs->dir;
+	}
+
+	/*
+	 * Reset open drain and altsel configs to workaround improper
+	 * reset values or unwanted modifications by BootROM
+	 */
+	ltq_clrbits(gpio_od, gpio_to_bit(gpio));
+	ltq_clrbits(gpio_altsel0, gpio_to_bit(gpio));
+	ltq_clrbits(gpio_altsel1, gpio_to_bit(gpio));
+
+	/* Switch to input */
+	ltq_clrbits(gpio_dir, gpio_to_bit(gpio));
+
+	return 0;
+}
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+	unsigned port = gpio_to_port(gpio);
+	const void *gpio_od = &ltq_gpio_regs->ports[port].od;
+	const void *gpio_altsel0 = &ltq_gpio_regs->ports[port].altsel0;
+	const void *gpio_altsel1 = &ltq_gpio_regs->ports[port].altsel1;
+	const void *gpio_dir = &ltq_gpio_regs->ports[port].dir;
+	const void *gpio_out = &ltq_gpio_regs->ports[port].out;
+	u32 data = gpio_to_bit(gpio);
+
+	if (is_gpio_ssio(gpio)) {
+		data = ssio_gpio_to_bit(gpio);
+		if (value)
+			ltq_setbits(&ltq_ssio_regs->cpu0, data);
+		else
+			ltq_clrbits(&ltq_ssio_regs->cpu0, data);
+
+		return 0;
+	}
+
+	if (is_gpio_bank3(port)) {
+		gpio_od = &ltq_gpio3_regs->od;
+		gpio_altsel0 = &ltq_gpio3_regs->altsel0;
+		gpio_altsel1 = &ltq_gpio3_regs->altsel1;
+		gpio_dir = &ltq_gpio3_regs->dir;
+		gpio_out = &ltq_gpio3_regs->out;
+	}
+
+	/*
+	 * Reset open drain and altsel configs to workaround improper
+	 * reset values or unwanted modifications by BootROM
+	 */
+	ltq_setbits(gpio_od, data);
+	ltq_clrbits(gpio_altsel0, data);
+	ltq_clrbits(gpio_altsel1, data);
+
+	if (value)
+		ltq_setbits(gpio_out, data);
+	else
+		ltq_clrbits(gpio_out, data);
+
+	/* Switch to output */
+	ltq_setbits(gpio_dir, data);
+
+	return 0;
+}
+
+int gpio_get_value(unsigned gpio)
+{
+	unsigned port = gpio_to_port(gpio);
+	const void *gpio_in = &ltq_gpio_regs->ports[port].in;
+	u32 data = gpio_to_bit(gpio);
+	u32 val;
+
+	if (is_gpio_ssio(gpio)) {
+		gpio_in = &ltq_ssio_regs->cpu0;
+		data = ssio_gpio_to_bit(gpio);
+	}
+
+	if (is_gpio_bank3(port))
+		gpio_in = &ltq_gpio3_regs->in;
+
+	val = ltq_readl(gpio_in);
+
+	return !!(val & data);
+}
+
+int gpio_set_value(unsigned gpio, int value)
+{
+	unsigned port = gpio_to_port(gpio);
+	const void *gpio_out = &ltq_gpio_regs->ports[port].out;
+	u32 data = gpio_to_bit(gpio);
+
+	if (is_gpio_ssio(gpio)) {
+		gpio_out = &ltq_ssio_regs->cpu0;
+		data = ssio_gpio_to_bit(gpio);
+	}
+
+	if (is_gpio_bank3(port))
+		gpio_out = &ltq_gpio3_regs->out;
+
+	if (value)
+		ltq_setbits(gpio_out, data);
+	else
+		ltq_clrbits(gpio_out, data);
+
+	return 0;
+}
+
+int gpio_set_altfunc(unsigned gpio, int altsel0, int altsel1, int dir)
+{
+	unsigned port = gpio_to_port(gpio);
+	const void *gpio_od = &ltq_gpio_regs->ports[port].od;
+	const void *gpio_altsel0 = &ltq_gpio_regs->ports[port].altsel0;
+	const void *gpio_altsel1 = &ltq_gpio_regs->ports[port].altsel1;
+	const void *gpio_dir = &ltq_gpio_regs->ports[port].dir;
+
+	if (is_gpio_ssio(gpio))
+		return 0;
+
+	if (is_gpio_bank3(port)) {
+		gpio_od = &ltq_gpio3_regs->od;
+		gpio_altsel0 = &ltq_gpio3_regs->altsel0;
+		gpio_altsel1 = &ltq_gpio3_regs->altsel1;
+		gpio_dir = &ltq_gpio3_regs->dir;
+	}
+
+	if (altsel0)
+		ltq_setbits(gpio_altsel0, gpio_to_bit(gpio));
+	else
+		ltq_clrbits(gpio_altsel0, gpio_to_bit(gpio));
+
+	if (altsel1)
+		ltq_setbits(gpio_altsel1, gpio_to_bit(gpio));
+	else
+		ltq_clrbits(gpio_altsel1, gpio_to_bit(gpio));
+
+	if (dir) {
+		ltq_setbits(gpio_od, gpio_to_bit(gpio));
+		ltq_setbits(gpio_dir, gpio_to_bit(gpio));
+	} else {
+		ltq_clrbits(gpio_od, gpio_to_bit(gpio));
+		ltq_clrbits(gpio_dir, gpio_to_bit(gpio));
+	}
+
+	return 0;
+}
+
+int gpio_set_opendrain(unsigned gpio, int od)
+{
+	unsigned port = gpio_to_port(gpio);
+	const void *gpio_od = &ltq_gpio_regs->ports[port].od;
+
+	if (is_gpio_ssio(gpio))
+		return 0;
+
+	if (is_gpio_bank3(port))
+		gpio_od = &ltq_gpio3_regs->od;
+
+	if (od)
+		ltq_setbits(gpio_od, gpio_to_bit(gpio));
+	else
+		ltq_clrbits(gpio_od, gpio_to_bit(gpio));
+
+	return 0;
+}
--- a/drivers/mtd/cfi_flash.c
+++ b/drivers/mtd/cfi_flash.c
@@ -161,6 +161,18 @@ u64 flash_read64(void *addr)__attribute_
 #define flash_read64	__flash_read64
 #endif
 
+static inline void *__flash_swap_addr(unsigned long addr)
+{
+	return (void *) addr;
+}
+
+#ifdef CONFIG_CFI_FLASH_USE_WEAK_ADDR_SWAP
+void *flash_swap_addr(unsigned long addr)
+		__attribute__((weak, alias("__flash_swap_addr")));
+#else
+#define flash_swap_addr	__flash_swap_addr
+#endif
+
 /*-----------------------------------------------------------------------
  */
 #if defined(CONFIG_ENV_IS_IN_FLASH) || defined(CONFIG_ENV_ADDR_REDUND) || (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE)
@@ -196,7 +208,7 @@ flash_map (flash_info_t * info, flash_se
 {
 	unsigned int byte_offset = offset * info->portwidth;
 
-	return (void *)(info->start[sect] + byte_offset);
+	return flash_swap_addr(info->start[sect] + byte_offset);
 }
 
 static inline void flash_unmap(flash_info_t *info, flash_sect_t sect,
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -53,6 +53,7 @@ COBJS-$(CONFIG_NAND_JZ4740) += jz4740_na
 COBJS-$(CONFIG_NAND_KB9202) += kb9202_nand.o
 COBJS-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o
 COBJS-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o
+COBJS-$(CONFIG_NAND_LANTIQ) += lantiq_nand.o
 COBJS-$(CONFIG_NAND_MPC5121_NFC) += mpc5121_nfc.o
 COBJS-$(CONFIG_NAND_MXC) += mxc_nand.o
 COBJS-$(CONFIG_NAND_MXS) += mxs_nand.o
--- /dev/null
+++ b/drivers/mtd/nand/lantiq_nand.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/mtd/nand.h>
+#include <linux/compiler.h>
+#include <asm/arch/soc.h>
+#include <asm/arch/nand.h>
+#include <asm/lantiq/io.h>
+
+#define NAND_CON_ECC_ON		(1 << 31)
+#define NAND_CON_LATCH_PRE	(1 << 23)
+#define NAND_CON_LATCH_WP	(1 << 22)
+#define NAND_CON_LATCH_SE	(1 << 21)
+#define NAND_CON_LATCH_CS	(1 << 20)
+#define NAND_CON_LATCH_CLE	(1 << 19)
+#define NAND_CON_LATCH_ALE	(1 << 18)
+#define NAND_CON_OUT_CS1	(1 << 10)
+#define NAND_CON_IN_CS1		(1 << 8)
+#define NAND_CON_PRE_P		(1 << 7)
+#define NAND_CON_WP_P		(1 << 6)
+#define NAND_CON_SE_P		(1 << 5)
+#define NAND_CON_CS_P		(1 << 4)
+#define NAND_CON_CLE_P		(1 << 3)
+#define NAND_CON_ALE_P		(1 << 2)
+#define NAND_CON_CSMUX		(1 << 1)
+#define NAND_CON_NANDM		(1 << 0)
+
+#define NAND_WAIT_WR_C		(1 << 3)
+#define NAND_WAIT_RDBY		(1 << 0)
+
+#define NAND_CMD_ALE		(1 << 2)
+#define NAND_CMD_CLE		(1 << 3)
+#define NAND_CMD_CS		(1 << 4)
+#define NAND_CMD_SE		(1 << 5)
+#define NAND_CMD_WP		(1 << 6)
+#define NAND_CMD_PRE		(1 << 7)
+
+struct ltq_nand_regs {
+	__be32	con;		/* NAND controller control */
+	__be32	wait;		/* NAND Flash Device RD/BY State */
+	__be32	ecc0;		/* NAND Flash ECC Register 0 */
+	__be32	ecc_ac;		/* NAND Flash ECC Register address counter */
+	__be32	ecc_cr;		/* NAND Flash ECC Comparison */
+};
+
+static struct ltq_nand_regs *ltq_nand_regs =
+	(struct ltq_nand_regs *) CKSEG1ADDR(LTQ_EBU_NAND_BASE);
+
+static void ltq_nand_wait_ready(void)
+{
+	while ((ltq_readl(&ltq_nand_regs->wait) & NAND_WAIT_WR_C) == 0)
+		;
+}
+
+static int ltq_nand_dev_ready(struct mtd_info *mtd)
+{
+	u32 data = ltq_readl(&ltq_nand_regs->wait);
+	return data & NAND_WAIT_RDBY;
+}
+
+static void ltq_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+	if (chip == 0) {
+		ltq_setbits(&ltq_nand_regs->con, NAND_CON_NANDM);
+		ltq_setbits(&ltq_nand_regs->con, NAND_CON_LATCH_CS);
+	} else {
+		ltq_clrbits(&ltq_nand_regs->con, NAND_CON_LATCH_CS);
+		ltq_clrbits(&ltq_nand_regs->con, NAND_CON_NANDM);
+	}
+}
+
+static void ltq_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+	struct nand_chip *chip = mtd->priv;
+	unsigned long addr = (unsigned long) chip->IO_ADDR_W;
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		if (ctrl & NAND_ALE)
+			addr |= NAND_CMD_ALE;
+		else
+			addr &= ~NAND_CMD_ALE;
+
+		if (ctrl & NAND_CLE)
+			addr |= NAND_CMD_CLE;
+		else
+			addr &= ~NAND_CMD_CLE;
+
+		chip->IO_ADDR_W = (void __iomem *) addr;
+	}
+
+	if (cmd != NAND_CMD_NONE) {
+		writeb(cmd, chip->IO_ADDR_W);
+		ltq_nand_wait_ready();
+	}
+}
+
+int ltq_nand_init(struct nand_chip *nand)
+{
+	/* Enable NAND, set NAND CS to EBU CS1, enable EBU CS mux */
+	ltq_writel(&ltq_nand_regs->con, NAND_CON_OUT_CS1 | NAND_CON_IN_CS1 |
+		NAND_CON_PRE_P | NAND_CON_WP_P | NAND_CON_SE_P |
+		NAND_CON_CS_P | NAND_CON_CSMUX);
+
+	nand->dev_ready = ltq_nand_dev_ready;
+	nand->select_chip = ltq_nand_select_chip;
+	nand->cmd_ctrl = ltq_nand_cmd_ctrl;
+
+	nand->chip_delay = 30;
+	nand->options = 0;
+	nand->ecc.mode = NAND_ECC_SOFT;
+
+	/* Enable CS bit in address offset */
+	nand->IO_ADDR_R = nand->IO_ADDR_R + NAND_CMD_CS;
+	nand->IO_ADDR_W = nand->IO_ADDR_W + NAND_CMD_CS;
+
+	return 0;
+}
+
+__weak int board_nand_init(struct nand_chip *chip)
+{
+	return ltq_nand_init(chip);
+}
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -37,6 +37,8 @@ COBJS-$(CONFIG_INCA_IP_SWITCH) += inca-i
 COBJS-$(CONFIG_DRIVER_KS8695ETH) += ks8695eth.o
 COBJS-$(CONFIG_KS8851_MLL) += ks8851_mll.o
 COBJS-$(CONFIG_LAN91C96) += lan91c96.o
+COBJS-$(CONFIG_LANTIQ_DANUBE_ETOP) += lantiq_danube_etop.o
+COBJS-$(CONFIG_LANTIQ_VRX200_SWITCH) += lantiq_vrx200_switch.o
 COBJS-$(CONFIG_MACB) += macb.o
 COBJS-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
 COBJS-$(CONFIG_MPC5xxx_FEC) += mpc5xxx_fec.o
--- /dev/null
+++ b/drivers/net/lantiq_danube_etop.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <netdev.h>
+#include <miiphy.h>
+#include <switch.h>
+#include <asm/lantiq/io.h>
+#include <asm/lantiq/eth.h>
+#include <asm/lantiq/pm.h>
+#include <asm/lantiq/reset.h>
+#include <asm/lantiq/dma.h>
+#include <asm/arch/soc.h>
+
+#define LTQ_PPE_ETOP_MDIO_ACC_RA	(1 << 31)
+#define LTQ_PPE_ETOP_MDIO_CFG_UMM1	(1 << 2)
+#define LTQ_PPE_ETOP_MDIO_CFG_UMM0	(1 << 1)
+
+#define LTQ_PPE_ETOP_CFG_TCKINV1	(1 << 11)
+#define LTQ_PPE_ETOP_CFG_TCKINV0	(1 << 10)
+#define LTQ_PPE_ETOP_CFG_FEN1		(1 << 9)
+#define LTQ_PPE_ETOP_CFG_FEN0		(1 << 8)
+#define LTQ_PPE_ETOP_CFG_SEN1		(1 << 7)
+#define LTQ_PPE_ETOP_CFG_SEN0		(1 << 6)
+#define LTQ_PPE_ETOP_CFG_TURBO1		(1 << 5)
+#define LTQ_PPE_ETOP_CFG_REMII1		(1 << 4)
+#define LTQ_PPE_ETOP_CFG_OFF1		(1 << 3)
+#define LTQ_PPE_ETOP_CFG_TURBO0		(1 << 2)
+#define LTQ_PPE_ETOP_CFG_REMII0		(1 << 1)
+#define LTQ_PPE_ETOP_CFG_OFF0		(1 << 0)
+
+#define LTQ_PPE_ENET0_MAC_CFG_CGEN	(1 << 11)
+#define LTQ_PPE_ENET0_MAC_CFG_DUPLEX	(1 << 2)
+#define LTQ_PPE_ENET0_MAC_CFG_SPEED	(1 << 1)
+#define LTQ_PPE_ENET0_MAC_CFG_LINK	(1 << 0)
+
+#define LTQ_PPE_ENETS0_CFG_FTUC		(1 << 28)
+
+#define LTQ_ETH_RX_BUFFER_CNT		PKTBUFSRX
+#define LTQ_ETH_TX_BUFFER_CNT		8
+#define LTQ_ETH_RX_DATA_SIZE		PKTSIZE_ALIGN
+#define LTQ_ETH_IP_ALIGN		2
+
+#define LTQ_MDIO_DRV_NAME		"ltq-mdio"
+#define LTQ_ETH_DRV_NAME		"ltq-eth"
+
+struct ltq_ppe_etop_regs {
+	u32	mdio_cfg;		/* MDIO configuration */
+	u32	mdio_acc;		/* MDIO access */
+	u32	cfg;			/* ETOP configuration */
+	u32	ig_vlan_cos;		/* IG VLAN priority CoS mapping */
+	u32	ig_dscp_cos3;		/* IG DSCP CoS mapping 3 */
+	u32	ig_dscp_cos2;		/* IG DSCP CoS mapping 2 */
+	u32	ig_dscp_cos1;		/* IG DSCP CoS mapping 1 */
+	u32	ig_dscp_cos0;		/* IG DSCP CoS mapping 0 */
+	u32	ig_plen_ctrl;		/* IG frame length control */
+	u32	rsvd0[3];
+	u32	vpid;			/* VLAN protocol ID */
+};
+
+struct ltq_ppe_enet_regs {
+	u32	mac_cfg;		/* MAC configuration */
+	u32	rsvd0[3];
+	u32	ig_cfg;			/* Ingress configuration */
+	u32	ig_pgcnt;		/* Ingress buffer used page count */
+	u32	rsvd1;
+	u32	ig_buf_ctrl;		/* Ingress buffer backpressure ctrl */
+	u32	cos_cfg;		/* Classification configuration */
+	u32	ig_drop;		/* Total ingress drop frames */
+	u32	ig_err;			/* Total ingress error frames */
+	u32	mac_da0;		/* Ingress MAC address 0 */
+	u32	mac_da1;		/* Ingress MAC address 1 */
+	u32	rsvd2[22];
+	u32	pgcnt;			/* Page counter */
+	u32	rsvd3;
+	u32	hf_ctrl;		/* Half duplex control */
+	u32	tx_ctrl;		/* Transmit control */
+	u32	rsvd4;
+	u32	vlcos0;			/* VLAN insertion config CoS 0 */
+	u32	vlcos1;			/* VLAN insertion config CoS 1 */
+	u32	vlcos2;			/* VLAN insertion config CoS 2 */
+	u32	vlcos3;			/* VLAN insertion config CoS 3 */
+	u32	eg_col;			/* Total egress collision frames */
+	u32	eg_drop;		/* Total egress drop frames */
+};
+
+struct ltq_eth_priv {
+	struct ltq_dma_device dma_dev;
+	struct mii_dev *bus;
+	struct eth_device *dev;
+	int rx_num;
+	int tx_num;
+};
+
+struct ltq_mdio_access {
+	union {
+		struct {
+			unsigned ra:1;
+			unsigned rw:1;
+			unsigned rsvd:4;
+			unsigned phya:5;
+			unsigned rega:5;
+			unsigned phyd:16;
+		} reg;
+		u32 val;
+	};
+};
+
+static struct ltq_ppe_etop_regs *ltq_ppe_etop_regs =
+	(struct ltq_ppe_etop_regs *) CKSEG1ADDR(LTQ_PPE_ETOP_BASE);
+
+static struct ltq_ppe_enet_regs *ltq_ppe_enet0_regs =
+	(struct ltq_ppe_enet_regs *) CKSEG1ADDR(LTQ_PPE_ENET0_BASE);
+
+static inline int ltq_mdio_poll(void)
+{
+	struct ltq_mdio_access acc;
+	unsigned cnt = 10000;
+
+	while (likely(cnt--)) {
+		acc.val = ltq_readl(&ltq_ppe_etop_regs->mdio_acc);
+		if (!acc.reg.ra)
+			return 0;
+	}
+
+	return 1;
+}
+
+static int ltq_mdio_read(struct mii_dev *bus, int addr, int dev_addr,
+				int regnum)
+{
+	struct ltq_mdio_access acc;
+	int ret;
+
+	acc.val = 0;
+	acc.reg.ra = 1;
+	acc.reg.rw = 1;
+	acc.reg.phya = addr;
+	acc.reg.rega = regnum;
+
+	ret = ltq_mdio_poll();
+	if (ret)
+		return ret;
+
+	ltq_writel(&ltq_ppe_etop_regs->mdio_acc, acc.val);
+
+	ret = ltq_mdio_poll();
+	if (ret)
+		return ret;
+
+	acc.val = ltq_readl(&ltq_ppe_etop_regs->mdio_acc);
+
+	return acc.reg.phyd;
+}
+
+static int ltq_mdio_write(struct mii_dev *bus, int addr, int dev_addr,
+				int regnum, u16 val)
+{
+	struct ltq_mdio_access acc;
+	int ret;
+
+	acc.val = 0;
+	acc.reg.ra = 1;
+	acc.reg.rw = 0;
+	acc.reg.phya = addr;
+	acc.reg.rega = regnum;
+	acc.reg.phyd = val;
+
+	ret = ltq_mdio_poll();
+	if (ret)
+		return ret;
+
+	ltq_writel(&ltq_ppe_etop_regs->mdio_acc, acc.val);
+
+	return 0;
+}
+
+static inline void ltq_eth_write_hwaddr(const struct eth_device *dev)
+{
+	u32 da0, da1;
+
+	da0 = (dev->enetaddr[0] << 24) + (dev->enetaddr[1] << 16) +
+		(dev->enetaddr[2] << 8) + dev->enetaddr[3];
+	da1 = (dev->enetaddr[4] << 24) + (dev->enetaddr[5] << 16);
+
+	ltq_writel(&ltq_ppe_enet0_regs->mac_da0, da0);
+	ltq_writel(&ltq_ppe_enet0_regs->mac_da1, da1);
+}
+
+static inline u8 *ltq_eth_rx_packet_align(int rx_num)
+{
+	u8 *packet = (u8 *) NetRxPackets[rx_num];
+
+	/*
+	 * IP header needs
+	 */
+	return packet + LTQ_ETH_IP_ALIGN;
+}
+
+static int ltq_eth_init(struct eth_device *dev, bd_t *bis)
+{
+	struct ltq_eth_priv *priv = dev->priv;
+	struct ltq_dma_device *dma_dev = &priv->dma_dev;
+	int i;
+
+	ltq_eth_write_hwaddr(dev);
+
+	for (i = 0; i < LTQ_ETH_RX_BUFFER_CNT; i++)
+		ltq_dma_rx_map(dma_dev, i, ltq_eth_rx_packet_align(i),
+			LTQ_ETH_RX_DATA_SIZE);
+
+	ltq_dma_enable(dma_dev);
+
+	priv->rx_num = 0;
+	priv->tx_num = 0;
+
+	return 0;
+}
+
+static void ltq_eth_halt(struct eth_device *dev)
+{
+	struct ltq_eth_priv *priv = dev->priv;
+	struct ltq_dma_device *dma_dev = &priv->dma_dev;
+
+	ltq_dma_reset(dma_dev);
+}
+
+static int ltq_eth_send(struct eth_device *dev, void *packet, int length)
+{
+	struct ltq_eth_priv *priv = dev->priv;
+	struct ltq_dma_device *dma_dev = &priv->dma_dev;
+	int err;
+
+	/* Minimum payload length w/ CRC is 60 bytes */
+	if (length < 60)
+		length = 60;
+
+	err = ltq_dma_tx_map(dma_dev, priv->tx_num, packet, length, 10);
+	if (err) {
+		puts("NET: timeout on waiting for TX descriptor\n");
+		return -1;
+	}
+
+	priv->tx_num = (priv->tx_num + 1) % LTQ_ETH_TX_BUFFER_CNT;
+
+	return err;
+}
+
+static int ltq_eth_recv(struct eth_device *dev)
+{
+	struct ltq_eth_priv *priv = dev->priv;
+	struct ltq_dma_device *dma_dev = &priv->dma_dev;
+	u8 *packet;
+	int len;
+
+	if (!ltq_dma_rx_poll(dma_dev, priv->rx_num))
+		return 0;
+
+#if 0
+	printf("%s: rx_num %d\n", __func__, priv->rx_num);
+#endif
+
+	len = ltq_dma_rx_length(dma_dev, priv->rx_num);
+	packet = ltq_eth_rx_packet_align(priv->rx_num);
+
+#if 0
+	printf("%s: received: packet %p, len %u, rx_num %d\n",
+		__func__, packet, len, priv->rx_num);
+#endif
+
+	if (len)
+		NetReceive(packet, len);
+
+	ltq_dma_rx_map(dma_dev, priv->rx_num, packet,
+		LTQ_ETH_RX_DATA_SIZE);
+
+	priv->rx_num = (priv->rx_num + 1) % LTQ_ETH_RX_BUFFER_CNT;
+
+	return 0;
+}
+
+static void ltq_eth_hw_init(const struct ltq_eth_port_config *port)
+{
+	u32 data;
+
+	/* Power up ethernet subsystems */
+	ltq_pm_enable(LTQ_PM_ETH);
+
+	/* Reset ethernet subsystems */
+	ltq_reset_once(LTQ_RESET_ETH, 1);
+
+	/* Disable MDIO auto-detection */
+	ltq_clrbits(&ltq_ppe_etop_regs->mdio_cfg, LTQ_PPE_ETOP_MDIO_CFG_UMM1 |
+			LTQ_PPE_ETOP_MDIO_CFG_UMM0);
+
+	/* Enable CRC generation, Full Duplex, 100Mbps, Link up */
+	ltq_writel(&ltq_ppe_enet0_regs->mac_cfg, LTQ_PPE_ENET0_MAC_CFG_CGEN |
+			LTQ_PPE_ENET0_MAC_CFG_DUPLEX |
+			LTQ_PPE_ENET0_MAC_CFG_SPEED |
+			LTQ_PPE_ENET0_MAC_CFG_LINK);
+
+	/* Reset ETOP cfg and disable all */
+	data = LTQ_PPE_ETOP_CFG_OFF0 | LTQ_PPE_ETOP_CFG_OFF1;
+
+	/* Enable ENET0, enable store and fetch */
+	data &= ~LTQ_PPE_ETOP_CFG_OFF0;
+	data |= LTQ_PPE_ETOP_CFG_SEN0 | LTQ_PPE_ETOP_CFG_FEN0;
+
+	if (port->phy_if == PHY_INTERFACE_MODE_RMII)
+		data |= LTQ_PPE_ETOP_CFG_REMII0;
+	else
+		data &= ~LTQ_PPE_ETOP_CFG_REMII0;
+
+	ltq_writel(&ltq_ppe_etop_regs->cfg, data);
+
+	/* Set allowed packet length from 64 bytes to 1518 bytes */
+	ltq_writel(&ltq_ppe_etop_regs->ig_plen_ctrl, (64 << 16) | 1518);
+
+	/* Enable filter for unicast packets */
+	ltq_setbits(&ltq_ppe_enet0_regs->ig_cfg, LTQ_PPE_ENETS0_CFG_FTUC);
+}
+
+int ltq_eth_initialize(const struct ltq_eth_board_config *board_config)
+{
+	struct eth_device *dev;
+	struct mii_dev *bus;
+	struct ltq_eth_priv *priv;
+	struct ltq_dma_device *dma_dev;
+	const struct ltq_eth_port_config *port = &board_config->ports[0];
+	struct phy_device *phy;
+	struct switch_device *sw;
+	int ret;
+
+	ltq_dma_init();
+	ltq_eth_hw_init(port);
+
+	dev = calloc(1, sizeof(*dev));
+	if (!dev)
+		return -1;
+
+	priv = calloc(1, sizeof(*priv));
+	if (!priv)
+		return -1;
+
+	bus = mdio_alloc();
+	if (!bus)
+		return -1;
+
+	sprintf(dev->name, LTQ_ETH_DRV_NAME);
+	dev->priv = priv;
+	dev->init = ltq_eth_init;
+	dev->halt = ltq_eth_halt;
+	dev->recv = ltq_eth_recv;
+	dev->send = ltq_eth_send;
+
+	sprintf(bus->name, LTQ_MDIO_DRV_NAME);
+	bus->read = ltq_mdio_read;
+	bus->write = ltq_mdio_write;
+	bus->priv = priv;
+
+	dma_dev = &priv->dma_dev;
+	dma_dev->port = 0;
+	dma_dev->rx_chan.chan_no = 6;
+	dma_dev->rx_chan.class = 3;
+	dma_dev->rx_chan.num_desc = LTQ_ETH_RX_BUFFER_CNT;
+	dma_dev->rx_endian_swap = LTQ_DMA_ENDIANESS_B3_B2_B1_B0;
+	dma_dev->rx_burst_len = LTQ_DMA_BURST_2WORDS;
+	dma_dev->tx_chan.chan_no = 7;
+	dma_dev->tx_chan.class = 3;
+	dma_dev->tx_chan.num_desc = LTQ_ETH_TX_BUFFER_CNT;
+	dma_dev->tx_endian_swap = LTQ_DMA_ENDIANESS_B3_B2_B1_B0;
+	dma_dev->tx_burst_len = LTQ_DMA_BURST_2WORDS;
+
+	priv->bus = bus;
+	priv->dev = dev;
+
+	ret = ltq_dma_register(dma_dev);
+	if (ret)
+		return ret;
+
+	ret = mdio_register(bus);
+	if (ret)
+		return ret;
+
+	ret = eth_register(dev);
+	if (ret)
+		return ret;
+
+	if (port->flags & LTQ_ETH_PORT_SWITCH) {
+		sw = switch_connect(bus);
+		if (!sw)
+			return -1;
+
+		switch_setup(sw);
+	}
+
+	if (port->flags & LTQ_ETH_PORT_PHY) {
+		phy = phy_connect(bus, port->phy_addr, dev, port->phy_if);
+		if (!phy)
+			return -1;
+
+		phy_config(phy);
+	}
+
+	return 0;
+}
--- /dev/null
+++ b/drivers/net/lantiq_vrx200_switch.c
@@ -0,0 +1,675 @@
+/*
+ * Copyright (C) 2010-2011 Lantiq Deutschland GmbH
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#define DEBUG
+
+#include <common.h>
+#include <malloc.h>
+#include <netdev.h>
+#include <miiphy.h>
+#include <linux/compiler.h>
+#include <asm/gpio.h>
+#include <asm/processor.h>
+#include <asm/lantiq/io.h>
+#include <asm/lantiq/eth.h>
+#include <asm/lantiq/pm.h>
+#include <asm/lantiq/reset.h>
+#include <asm/lantiq/dma.h>
+#include <asm/arch/soc.h>
+#include <asm/arch/switch.h>
+
+#define LTQ_ETH_RX_BUFFER_CNT		PKTBUFSRX
+#define LTQ_ETH_TX_BUFFER_CNT		8
+#define LTQ_ETH_RX_DATA_SIZE		PKTSIZE_ALIGN
+#define LTQ_ETH_IP_ALIGN		2
+
+#define LTQ_MDIO_DRV_NAME		"ltq-mdio"
+#define LTQ_ETH_DRV_NAME		"ltq-eth"
+
+#define LTQ_ETHSW_MAX_GMAC		6
+#define LTQ_ETHSW_PMAC			6
+
+struct ltq_mdio_phy_addr_reg {
+	union {
+		struct {
+			unsigned rsvd:1;
+			unsigned lnkst:2;	/* Link status control */
+			unsigned speed:2;	/* Speed control */
+			unsigned fdup:2;	/* Full duplex control */
+			unsigned fcontx:2;	/* Flow control mode TX */
+			unsigned fconrx:2;	/* Flow control mode RX */
+			unsigned addr:5;	/* PHY address */
+		} bits;
+		u16 val;
+	};
+};
+
+enum ltq_mdio_phy_addr_lnkst {
+	LTQ_MDIO_PHY_ADDR_LNKST_AUTO = 0,
+	LTQ_MDIO_PHY_ADDR_LNKST_UP = 1,
+	LTQ_MDIO_PHY_ADDR_LNKST_DOWN = 2,
+};
+
+enum ltq_mdio_phy_addr_speed {
+	LTQ_MDIO_PHY_ADDR_SPEED_M10 = 0,
+	LTQ_MDIO_PHY_ADDR_SPEED_M100 = 1,
+	LTQ_MDIO_PHY_ADDR_SPEED_G1 = 2,
+	LTQ_MDIO_PHY_ADDR_SPEED_AUTO = 3,
+};
+
+enum ltq_mdio_phy_addr_fdup {
+	LTQ_MDIO_PHY_ADDR_FDUP_AUTO = 0,
+	LTQ_MDIO_PHY_ADDR_FDUP_ENABLE = 1,
+	LTQ_MDIO_PHY_ADDR_FDUP_DISABLE = 3,
+};
+
+enum ltq_mdio_phy_addr_fcon {
+	LTQ_MDIO_PHY_ADDR_FCON_AUTO = 0,
+	LTQ_MDIO_PHY_ADDR_FCON_ENABLE = 1,
+	LTQ_MDIO_PHY_ADDR_FCON_DISABLE = 3,
+};
+
+struct ltq_mii_mii_cfg_reg {
+	union {
+		struct {
+			unsigned res:1;		/* Hardware reset */
+			unsigned en:1;		/* xMII interface enable */
+			unsigned isol:1;	/* xMII interface isolate */
+			unsigned ldclkdis:1;	/* Link down clock disable */
+			unsigned rsvd:1;
+			unsigned crs:2;		/* CRS sensitivity config */
+			unsigned rgmii_ibs:1;	/* RGMII In Band status */
+			unsigned rmii:1;	/* RMII ref clock direction */
+			unsigned miirate:3;	/* xMII interface clock rate */
+			unsigned miimode:4;	/* xMII interface mode */
+		} bits;
+		u16 val;
+	};
+};
+
+enum ltq_mii_mii_cfg_miirate {
+	LTQ_MII_MII_CFG_MIIRATE_M2P5 = 0,
+	LTQ_MII_MII_CFG_MIIRATE_M25 = 1,
+	LTQ_MII_MII_CFG_MIIRATE_M125 = 2,
+	LTQ_MII_MII_CFG_MIIRATE_M50 = 3,
+	LTQ_MII_MII_CFG_MIIRATE_AUTO = 4,
+};
+
+enum ltq_mii_mii_cfg_miimode {
+	LTQ_MII_MII_CFG_MIIMODE_MIIP = 0,
+	LTQ_MII_MII_CFG_MIIMODE_MIIM = 1,
+	LTQ_MII_MII_CFG_MIIMODE_RMIIP = 2,
+	LTQ_MII_MII_CFG_MIIMODE_RMIIM = 3,
+	LTQ_MII_MII_CFG_MIIMODE_RGMII = 4,
+};
+
+struct ltq_eth_priv {
+	struct ltq_dma_device dma_dev;
+	struct mii_dev *bus;
+	struct eth_device *dev;
+	struct phy_device *phymap[LTQ_ETHSW_MAX_GMAC];
+	int rx_num;
+	int tx_num;
+};
+
+static struct vr9_switch_regs *switch_regs =
+	(struct vr9_switch_regs *) CKSEG1ADDR(LTQ_SWITCH_BASE);
+
+static inline void vr9_switch_sync(void)
+{
+	__asm__("sync");
+}
+
+static inline int vr9_switch_mdio_is_busy(void)
+{
+	u32 mdio_ctrl = ltq_readl(&switch_regs->mdio.mdio_ctrl);
+
+	return mdio_ctrl & MDIO_CTRL_MBUSY;
+}
+
+static inline void vr9_switch_mdio_poll(void)
+{
+	while (vr9_switch_mdio_is_busy())
+		cpu_relax();
+}
+
+static int vr9_switch_mdio_read(struct mii_dev *bus, int phyad, int devad,
+					int regad)
+{
+	u32 mdio_ctrl;
+	int retval;
+
+	mdio_ctrl = MDIO_CTRL_OP_READ |
+		((phyad << MDIO_CTRL_PHYAD_SHIFT) & MDIO_CTRL_PHYAD_MASK) |
+		(regad & MDIO_CTRL_REGAD_MASK);
+
+	vr9_switch_mdio_poll();
+	ltq_writel(&switch_regs->mdio.mdio_ctrl, mdio_ctrl);
+	vr9_switch_mdio_poll();
+	retval = ltq_readl(&switch_regs->mdio.mdio_read);
+
+	return retval;
+}
+
+static int vr9_switch_mdio_write(struct mii_dev *bus, int phyad, int devad,
+					int regad, u16 val)
+{
+	u32 mdio_ctrl;
+
+	mdio_ctrl = MDIO_CTRL_OP_WRITE |
+		((phyad << MDIO_CTRL_PHYAD_SHIFT) & MDIO_CTRL_PHYAD_MASK) |
+		(regad & MDIO_CTRL_REGAD_MASK);
+
+	vr9_switch_mdio_poll();
+	ltq_writel(&switch_regs->mdio.mdio_write, val);
+	ltq_writel(&switch_regs->mdio.mdio_ctrl, mdio_ctrl);
+
+	return 0;
+}
+
+static void ltq_eth_gmac_update(struct phy_device *phydev, int num)
+{
+	struct ltq_mdio_phy_addr_reg phy_addr_reg;
+	struct ltq_mii_mii_cfg_reg mii_cfg_reg;
+
+	phy_addr_reg.val = ltq_readl(to_mdio_phyaddr(switch_regs, num));
+
+	switch (num) {
+	case 0:
+	case 1:
+	case 5:
+		mii_cfg_reg.val = ltq_readl(to_mii_miicfg(switch_regs, num));
+		break;
+	default:
+		mii_cfg_reg.val = 0;
+		break;
+	}
+
+	phy_addr_reg.bits.addr = phydev->addr;
+
+	if (phydev->link)
+		phy_addr_reg.bits.lnkst = LTQ_MDIO_PHY_ADDR_LNKST_UP;
+	else
+		phy_addr_reg.bits.lnkst = LTQ_MDIO_PHY_ADDR_LNKST_DOWN;
+
+	switch (phydev->speed) {
+	case SPEED_1000:
+		phy_addr_reg.bits.speed = LTQ_MDIO_PHY_ADDR_SPEED_G1;
+		mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M125;
+		break;
+	case SPEED_100:
+		phy_addr_reg.bits.speed = LTQ_MDIO_PHY_ADDR_SPEED_M100;
+		switch (mii_cfg_reg.bits.miimode) {
+		case LTQ_MII_MII_CFG_MIIMODE_RMIIM:
+		case LTQ_MII_MII_CFG_MIIMODE_RMIIP:
+			mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M50;
+			break;
+		default:
+			mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M25;
+			break;
+		}
+		break;
+	default:
+		phy_addr_reg.bits.speed = LTQ_MDIO_PHY_ADDR_SPEED_M10;
+		mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M2P5;
+		break;
+	}
+
+	if (phydev->duplex == DUPLEX_FULL)
+		phy_addr_reg.bits.fdup = LTQ_MDIO_PHY_ADDR_FDUP_ENABLE;
+	else
+		phy_addr_reg.bits.fdup = LTQ_MDIO_PHY_ADDR_FDUP_DISABLE;
+
+	ltq_writel(to_mdio_phyaddr(switch_regs, num), phy_addr_reg.val);
+
+	switch (num) {
+	case 0:
+	case 1:
+	case 5:
+		ltq_writel(to_mii_miicfg(switch_regs, num), mii_cfg_reg.val);
+		break;
+	default:
+		break;
+	}
+}
+
+static inline u8 *ltq_eth_rx_packet_align(int rx_num)
+{
+	u8 *packet = (u8 *) NetRxPackets[rx_num];
+
+	/*
+	 * IP header needs
+	 */
+	return packet + LTQ_ETH_IP_ALIGN;
+}
+
+static int ltq_eth_init(struct eth_device *dev, bd_t *bis)
+{
+	struct ltq_eth_priv *priv = dev->priv;
+	struct ltq_dma_device *dma_dev = &priv->dma_dev;
+	struct phy_device *phydev;
+	int i;
+
+	for (i = 0; i < LTQ_ETHSW_MAX_GMAC; i++) {
+		phydev = priv->phymap[i];
+		if (!phydev)
+			continue;
+
+		phy_startup(phydev);
+		ltq_eth_gmac_update(phydev, i);
+	}
+
+	for (i = 0; i < LTQ_ETH_RX_BUFFER_CNT; i++)
+		ltq_dma_rx_map(dma_dev, i, ltq_eth_rx_packet_align(i),
+			LTQ_ETH_RX_DATA_SIZE);
+
+	ltq_dma_enable(dma_dev);
+
+	priv->rx_num = 0;
+	priv->tx_num = 0;
+
+	return 0;
+}
+
+static void ltq_eth_halt(struct eth_device *dev)
+{
+	struct ltq_eth_priv *priv = dev->priv;
+	struct ltq_dma_device *dma_dev = &priv->dma_dev;
+	struct phy_device *phydev;
+	int i;
+
+	ltq_dma_reset(dma_dev);
+
+	for (i = 0; i < LTQ_ETHSW_MAX_GMAC; i++) {
+		phydev = priv->phymap[i];
+		if (!phydev)
+			continue;
+
+		phy_shutdown(phydev);
+		phydev->link = 0;
+		ltq_eth_gmac_update(phydev, i);
+	}
+}
+
+static int ltq_eth_send(struct eth_device *dev, void *packet, int length)
+{
+	struct ltq_eth_priv *priv = dev->priv;
+	struct ltq_dma_device *dma_dev = &priv->dma_dev;
+
+#if 0
+	printf("%s: packet %p, len %d\n", __func__, packet, length);
+#endif
+
+	ltq_dma_tx_map(dma_dev, priv->tx_num, packet, length, 10);
+	priv->tx_num = (priv->tx_num + 1) % LTQ_ETH_TX_BUFFER_CNT;
+
+	return 0;
+}
+
+static int ltq_eth_recv(struct eth_device *dev)
+{
+	struct ltq_eth_priv *priv = dev->priv;
+	struct ltq_dma_device *dma_dev = &priv->dma_dev;
+	u8 *packet;
+	int len;
+
+	if (!ltq_dma_rx_poll(dma_dev, priv->rx_num))
+		return 0;
+
+#if 0
+	printf("%s: rx_num %d\n", __func__, priv->rx_num);
+#endif
+
+	len = ltq_dma_rx_length(dma_dev, priv->rx_num);
+	packet = ltq_eth_rx_packet_align(priv->rx_num);
+
+#if 0
+	printf("%s: received: packet %p, len %u, rx_num %d\n",
+		__func__, packet, len, priv->rx_num);
+#endif
+
+	if (len)
+		NetReceive(packet, len);
+
+	ltq_dma_rx_map(dma_dev, priv->rx_num, packet,
+		LTQ_ETH_RX_DATA_SIZE);
+
+	priv->rx_num = (priv->rx_num + 1) % LTQ_ETH_RX_BUFFER_CNT;
+
+	return 0;
+}
+
+static void ltq_eth_gmac_init(int num)
+{
+	struct ltq_mdio_phy_addr_reg phy_addr_reg;
+	struct ltq_mii_mii_cfg_reg mii_cfg_reg;
+
+	/* Reset PHY status to link down */
+	phy_addr_reg.val = ltq_readl(to_mdio_phyaddr(switch_regs, num));
+	phy_addr_reg.bits.addr = num;
+	phy_addr_reg.bits.lnkst = LTQ_MDIO_PHY_ADDR_LNKST_DOWN;
+	phy_addr_reg.bits.speed = LTQ_MDIO_PHY_ADDR_SPEED_M10;
+	phy_addr_reg.bits.fdup = LTQ_MDIO_PHY_ADDR_FDUP_DISABLE;
+	ltq_writel(to_mdio_phyaddr(switch_regs, num), phy_addr_reg.val);
+
+	/* Reset and disable MII interface */
+	switch (num) {
+	case 0:
+	case 1:
+	case 5:
+		mii_cfg_reg.val = ltq_readl(to_mii_miicfg(switch_regs, num));
+		mii_cfg_reg.bits.en = 0;
+		mii_cfg_reg.bits.res = 1;
+		mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M2P5;
+		ltq_writel(to_mii_miicfg(switch_regs, num), mii_cfg_reg.val);
+		break;
+	default:
+		break;
+	}
+
+	/*
+	 * - enable frame checksum generation
+	 * - enable padding of short frames
+	 * - disable flow control
+	 */
+	ltq_writel(to_mac_ctrl(switch_regs, num, 0),
+		MAC_CTRL0_PADEN | MAC_CTRL0_FCS | MAC_CTRL0_FCON_NONE);
+
+	vr9_switch_sync();
+}
+
+static void ltq_eth_pmac_init(void)
+{
+	/*
+	 * WAR: buffer congestion:
+	 * - shorten preambel to 1 byte
+	 * - set TX IPG to 7 bytes
+	 */
+#if 1
+	ltq_writel(to_mac_ctrl(switch_regs, LTQ_ETHSW_PMAC, 1),
+		MAC_CTRL1_SHORTPRE | 7);
+#endif
+
+	/*
+	 * WAR: systematical concept weakness ACM bug
+	 * - set maximum number of used buffer segments to 254
+	 * - soft-reset BM FSQM
+	 */
+#if 1
+	ltq_writel(&switch_regs->bm.core.fsqm_gctrl, 253);
+	ltq_setbits(&switch_regs->bm.core.gctrl, BM_GCTRL_F_SRES);
+	ltq_clrbits(&switch_regs->bm.core.gctrl, BM_GCTRL_F_SRES);
+#endif
+
+	/*
+	 * WAR: switch MAC drop bug
+	 */
+#if 1
+	ltq_writel(to_pce_tbl_key(switch_regs, 0), 0xf);
+	ltq_writel(to_pce_tbl_value(switch_regs, 0), 0x40);
+	ltq_writel(&switch_regs->pce.core.tbl_addr, 0x3);
+	ltq_writel(&switch_regs->pce.core.tbl_ctrl, 0x902f);
+#endif
+
+	/*
+	 * Configure frame header control:
+	 * - enable flow control
+	 * - enable CRC check for packets from DMA to PMAC
+	 * - remove special tag from packets from PMAC to DMA
+	 * - add CRC for packets from DMA to PMAC
+	 */
+	ltq_writel(&switch_regs->pmac.hd_ctl, /*PMAC_HD_CTL_FC |*/
+		PMAC_HD_CTL_CCRC | PMAC_HD_CTL_RST | PMAC_HD_CTL_AC |
+		PMAC_HD_CTL_RC);
+
+#if 1
+	ltq_writel(&switch_regs->pmac.rx_ipg, 0x8b);
+#endif
+
+	/*
+	 * - enable frame checksum generation
+	 * - enable padding of short frames
+	 * - disable flow control
+	 */
+	ltq_writel(to_mac_ctrl(switch_regs, LTQ_ETHSW_PMAC, 0),
+		MAC_CTRL0_PADEN | MAC_CTRL0_FCS | MAC_CTRL0_FCON_NONE);
+
+	vr9_switch_sync();
+}
+
+static void ltq_eth_hw_init(void)
+{
+	int i;
+
+	/* Power up ethernet and switch subsystems */
+	ltq_pm_enable(LTQ_PM_ETH);
+
+	/* Reset ethernet and switch subsystems */
+#if 0
+	ltq_reset_once(LTQ_RESET_ETH, 10);
+#endif
+
+	/* Enable switch macro */
+	ltq_setbits(&switch_regs->mdio.glob_ctrl, MDIO_GLOB_CTRL_SE);
+
+	/* Disable MDIO auto-polling for all ports */
+	ltq_writel(&switch_regs->mdio.mdc_cfg_0, 0);
+
+	/*
+	 * Enable and set MDIO management clock to 2.5 MHz. This is the
+	 * maximum clock for FE PHYs.
+	 * Formula for clock is:
+	 *
+	 *      50 MHz
+	 * x = ----------- - 1
+	 *      2 * f_MDC
+	 */
+	ltq_writel(&switch_regs->mdio.mdc_cfg_1, MDIO_MDC_CFG1_RES |
+		MDIO_MDC_CFG1_MCEN | 5);
+
+	vr9_switch_sync();
+
+	/* Init MAC connected to CPU  */
+	ltq_eth_pmac_init();
+
+	/* Init MACs connected to external MII interfaces */
+	for (i = 0; i < LTQ_ETHSW_MAX_GMAC; i++)
+		ltq_eth_gmac_init(i);
+}
+
+static void ltq_eth_port_config(struct ltq_eth_priv *priv,
+					const struct ltq_eth_port_config *port)
+{
+	struct ltq_mii_mii_cfg_reg mii_cfg_reg;
+	struct phy_device *phydev;
+	int setup_gpio = 0;
+
+	switch (port->num) {
+	case 0:	/* xMII0 */
+	case 1:	/* xMII1 */
+		mii_cfg_reg.val = ltq_readl(to_mii_miicfg(switch_regs,
+					port->num));
+		mii_cfg_reg.bits.en = port->flags ? 1 : 0;
+
+		switch (port->phy_if) {
+		case PHY_INTERFACE_MODE_MII:
+			if (port->flags & LTQ_ETH_PORT_PHY)
+				/* MII MAC mode, connected to external PHY */
+				mii_cfg_reg.bits.miimode =
+					LTQ_MII_MII_CFG_MIIMODE_MIIM;
+			else
+				/* MII PHY mode, connected to external MAC */
+				mii_cfg_reg.bits.miimode =
+					LTQ_MII_MII_CFG_MIIMODE_MIIP;
+			setup_gpio = 1;
+			break;
+		case PHY_INTERFACE_MODE_RMII:
+			if (port->flags & LTQ_ETH_PORT_PHY)
+				/* RMII MAC mode, connected to external PHY */
+				mii_cfg_reg.bits.miimode =
+					LTQ_MII_MII_CFG_MIIMODE_RMIIM;
+			else
+				/* RMII PHY mode, connected to external MAC */
+				mii_cfg_reg.bits.miimode =
+					LTQ_MII_MII_CFG_MIIMODE_RMIIP;
+			setup_gpio = 1;
+			break;
+		case PHY_INTERFACE_MODE_RGMII:
+			/* RGMII MAC mode, connected to external PHY */
+			mii_cfg_reg.bits.miimode =
+				LTQ_MII_MII_CFG_MIIMODE_RGMII;
+			setup_gpio = 1;
+
+			/* RGMII clock delays */
+			ltq_writel(to_mii_pcdu(switch_regs, port->num),
+				port->rgmii_rx_delay << PCDU_RXDLY_SHIFT |
+				port->rgmii_tx_delay);
+			break;
+		default:
+			break;
+		}
+
+		ltq_writel(to_mii_miicfg(switch_regs, port->num),
+			mii_cfg_reg.val);
+		break;
+	case 2:	/* internal GPHY0 */
+	case 3:	/* internal GPHY0 */
+	case 4:	/* internal GPHY1 */
+		switch (port->phy_if) {
+		case PHY_INTERFACE_MODE_MII:
+		case PHY_INTERFACE_MODE_GMII:
+			setup_gpio = 1;
+			break;
+		default:
+			break;
+		}
+		break;
+	case 5:	/* internal GPHY1 or xMII2 */
+		mii_cfg_reg.val = ltq_readl(to_mii_miicfg(switch_regs,
+					port->num));
+		mii_cfg_reg.bits.en = port->flags ? 1 : 0;
+
+		switch (port->phy_if) {
+		case PHY_INTERFACE_MODE_MII:
+			/* MII MAC mode, connected to internal GPHY */
+			mii_cfg_reg.bits.miimode =
+				LTQ_MII_MII_CFG_MIIMODE_MIIM;
+			setup_gpio = 1;
+			break;
+		case PHY_INTERFACE_MODE_RGMII:
+			/* RGMII MAC mode, connected to external PHY */
+			mii_cfg_reg.bits.miimode =
+				LTQ_MII_MII_CFG_MIIMODE_RGMII;
+			setup_gpio = 1;
+
+			/* RGMII clock delays */
+			ltq_writel(to_mii_pcdu(switch_regs, port->num),
+				port->rgmii_rx_delay << PCDU_RXDLY_SHIFT |
+				port->rgmii_tx_delay);
+			break;
+		default:
+			break;
+		}
+
+		ltq_writel(to_mii_miicfg(switch_regs, port->num),
+			mii_cfg_reg.val);
+		break;
+	default:
+		break;
+	}
+
+	/* Setup GPIOs for MII with external PHYs/MACs */
+	if (setup_gpio) {
+		/* MII/MDIO */
+		gpio_set_altfunc(42, GPIO_ALTSEL_SET, GPIO_ALTSEL_CLR,
+					GPIO_DIR_OUT);
+		/* MII/MDC */
+		gpio_set_altfunc(43, GPIO_ALTSEL_SET, GPIO_ALTSEL_CLR,
+					GPIO_DIR_OUT);
+	}
+
+	/* Connect to internal/external PHYs */
+	if (port->flags & LTQ_ETH_PORT_PHY) {
+		phydev = phy_connect(priv->bus, port->phy_addr, priv->dev,
+					port->phy_if);
+		if (phydev)
+			phy_config(phydev);
+
+		priv->phymap[port->num] = phydev;
+	}
+}
+
+int ltq_eth_initialize(const struct ltq_eth_board_config *board_config)
+{
+	struct eth_device *dev;
+	struct mii_dev *bus;
+	struct ltq_eth_priv *priv;
+	struct ltq_dma_device *dma_dev;
+	int i, ret;
+
+	build_check_vr9_registers();
+
+	ltq_dma_init();
+	ltq_eth_hw_init();
+
+	dev = calloc(1, sizeof(struct eth_device));
+	if (!dev)
+		return -1;
+
+	priv = calloc(1, sizeof(struct ltq_eth_priv));
+	if (!priv)
+		return -1;
+
+	bus = mdio_alloc();
+	if (!bus)
+		return -1;
+
+	sprintf(dev->name, LTQ_ETH_DRV_NAME);
+	dev->priv = priv;
+	dev->init = ltq_eth_init;
+	dev->halt = ltq_eth_halt;
+	dev->recv = ltq_eth_recv;
+	dev->send = ltq_eth_send;
+
+	sprintf(bus->name, LTQ_MDIO_DRV_NAME);
+	bus->read = vr9_switch_mdio_read;
+	bus->write = vr9_switch_mdio_write;
+	bus->priv = priv;
+
+	dma_dev = &priv->dma_dev;
+	dma_dev->port = 0;
+	dma_dev->rx_chan.chan_no = 0;
+	dma_dev->rx_chan.class = 0;
+	dma_dev->rx_chan.num_desc = LTQ_ETH_RX_BUFFER_CNT;
+	dma_dev->rx_endian_swap = LTQ_DMA_ENDIANESS_B3_B2_B1_B0;
+	dma_dev->rx_burst_len = LTQ_DMA_BURST_2WORDS;
+	dma_dev->tx_chan.chan_no = 1;
+	dma_dev->tx_chan.class = 0;
+	dma_dev->tx_chan.num_desc = LTQ_ETH_TX_BUFFER_CNT;
+	dma_dev->tx_endian_swap = LTQ_DMA_ENDIANESS_B3_B2_B1_B0;
+	dma_dev->tx_burst_len = LTQ_DMA_BURST_2WORDS;
+
+	priv->bus = bus;
+	priv->dev = dev;
+
+	ret = ltq_dma_register(dma_dev);
+	if (ret)
+		return -1;
+
+	ret = mdio_register(bus);
+	if (ret)
+		return -1;
+
+	ret = eth_register(dev);
+	if (ret)
+		return -1;
+
+	for (i = 0; i < board_config->num_ports; i++)
+		ltq_eth_port_config(priv, &board_config->ports[i]);
+
+	return 0;
+}
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -20,6 +20,7 @@ COBJS-$(CONFIG_PHY_BROADCOM) += broadcom
 COBJS-$(CONFIG_PHY_DAVICOM) += davicom.o
 COBJS-$(CONFIG_PHY_ET1011C) += et1011c.o
 COBJS-$(CONFIG_PHY_ICPLUS) += icplus.o
+COBJS-$(CONFIG_PHY_LANTIQ) += lantiq.o
 COBJS-$(CONFIG_PHY_LXT) += lxt.o
 COBJS-$(CONFIG_PHY_MARVELL) += marvell.o
 COBJS-$(CONFIG_PHY_MICREL) += micrel.o
--- /dev/null
+++ b/drivers/net/phy/lantiq.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#define DEBUG
+
+#include <common.h>
+#include <miiphy.h>
+
+#define ADVERTIZE_MPD		(1 << 10)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * Update link status.
+ *
+ * Based on genphy_update_link in phylib.c
+ */
+static int ltq_phy_update_link(struct phy_device *phydev)
+{
+	unsigned int mii_reg;
+
+	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+
+	/*
+	 * If we already saw the link up, and it hasn't gone down, then
+	 * we don't need to wait for autoneg again
+	 */
+	if (phydev->link && mii_reg & BMSR_LSTATUS)
+		return 0;
+
+	if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) {
+		phydev->link = 0;
+		return 0;
+	} else {
+		/* Read the link a second time to clear the latched state */
+		mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+
+		if (mii_reg & BMSR_LSTATUS)
+			phydev->link = 1;
+		else
+			phydev->link = 0;
+	}
+
+	return 0;
+}
+
+/*
+ * Update speed and duplex.
+ *
+ * Based on genphy_parse_link in phylib.c
+ */
+static int ltq_phy_parse_link(struct phy_device *phydev)
+{
+	unsigned int mii_reg;
+
+	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+
+	/* We're using autonegotiation */
+	if (mii_reg & BMSR_ANEGCAPABLE) {
+		u32 lpa = 0;
+		u32 gblpa = 0;
+
+		/* Check for gigabit capability */
+		if (mii_reg & BMSR_ERCAP) {
+			/* We want a list of states supported by
+			 * both PHYs in the link
+			 */
+			gblpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000);
+			gblpa &= phy_read(phydev,
+					MDIO_DEVAD_NONE, MII_CTRL1000) << 2;
+		}
+
+		/* Set the baseline so we only have to set them
+		 * if they're different
+		 */
+		phydev->speed = SPEED_10;
+		phydev->duplex = DUPLEX_HALF;
+
+		/* Check the gigabit fields */
+		if (gblpa & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) {
+			phydev->speed = SPEED_1000;
+
+			if (gblpa & PHY_1000BTSR_1000FD)
+				phydev->duplex = DUPLEX_FULL;
+
+			/* We're done! */
+			return 0;
+		}
+
+		lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);
+		lpa &= phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);
+
+		if (lpa & (LPA_100FULL | LPA_100HALF)) {
+			phydev->speed = SPEED_100;
+
+			if (lpa & LPA_100FULL)
+				phydev->duplex = DUPLEX_FULL;
+
+		} else if (lpa & LPA_10FULL)
+			phydev->duplex = DUPLEX_FULL;
+	} else {
+		u32 bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+
+		phydev->speed = SPEED_10;
+		phydev->duplex = DUPLEX_HALF;
+
+		if (bmcr & BMCR_FULLDPLX)
+			phydev->duplex = DUPLEX_FULL;
+
+		if (bmcr & BMCR_SPEED1000)
+			phydev->speed = SPEED_1000;
+		else if (bmcr & BMCR_SPEED100)
+			phydev->speed = SPEED_100;
+	}
+
+	return 0;
+}
+
+static int ltq_phy_config(struct phy_device *phydev)
+{
+	u16 val;
+
+	/* Advertise as Multi-port device */
+	val = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);
+	val |= ADVERTIZE_MPD;
+	phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, val);
+
+	genphy_config_aneg(phydev);
+
+	return 0;
+}
+
+static int ltq_phy_startup(struct phy_device *phydev)
+{
+	/*
+	 * Update PHY status immediately without any delays as genphy_startup
+	 * does because VRX200 switch needs to be configured dependent
+	 * on this information.
+	 */
+	ltq_phy_update_link(phydev);
+	ltq_phy_parse_link(phydev);
+
+	debug("ltq_phy: addr %d, link %d, speed %d, duplex %d\n",
+		phydev->addr, phydev->link, phydev->speed, phydev->duplex);
+
+	return 0;
+}
+
+static struct phy_driver xrx_11g_13_driver = {
+	.name = "Lantiq XWAY XRX PHY11G v1.3 and earlier",
+	.uid = 0x030260D0,
+	.mask = 0xFFFFFFF0,
+	.features = PHY_GBIT_FEATURES,
+	.config = ltq_phy_config,
+	.startup = ltq_phy_startup,
+	.shutdown = genphy_shutdown,
+};
+
+static struct phy_driver xrx_11g_14_driver = {
+	.name = "Lantiq XWAY XRX PHY11G v1.4 and later",
+	.uid = 0xd565a408,
+	.mask = 0xFFFFFFF8,
+	.features = PHY_GBIT_FEATURES,
+	.config = ltq_phy_config,
+	.startup = ltq_phy_startup,
+	.shutdown = genphy_shutdown,
+};
+
+static struct phy_driver xrx_22f_14_driver = {
+	.name = "Lantiq XWAY XRX PHY22F v1.4 and later",
+	.uid = 0xd565a418,
+	.mask = 0xFFFFFFF8,
+	.features = PHY_BASIC_FEATURES,
+	.config = ltq_phy_config,
+	.startup = ltq_phy_startup,
+	.shutdown = genphy_shutdown,
+};
+
+static struct phy_driver pef7071_driver = {
+	.name = "Lantiq XWAY PEF7071",
+	.uid = 0xd565a400,
+	.mask = 0xFFFFFFF8,
+	.features = PHY_GBIT_FEATURES,
+	.config = ltq_phy_config,
+	.startup = ltq_phy_startup,
+	.shutdown = genphy_shutdown,
+};
+
+static struct phy_driver xrx_genphy_driver = {
+	.name = "Generic PHY at Lantiq XWAY XRX switch",
+	.uid = 0,
+	.mask = 0,
+	.features = 0,
+	.config = genphy_config,
+	.startup = ltq_phy_startup,
+	.shutdown = genphy_shutdown,
+};
+
+int phy_lantiq_init(void)
+{
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
+	xrx_11g_13_driver.config = ltq_phy_config;
+	xrx_11g_13_driver.startup = ltq_phy_startup;
+	xrx_11g_13_driver.shutdown = genphy_shutdown;
+	xrx_11g_13_driver.name += gd->reloc_off;
+
+	xrx_11g_14_driver.config = ltq_phy_config;
+	xrx_11g_14_driver.startup = ltq_phy_startup;
+	xrx_11g_14_driver.shutdown = genphy_shutdown;
+	xrx_11g_14_driver.name += gd->reloc_off;
+
+	xrx_22f_14_driver.config = ltq_phy_config;
+	xrx_22f_14_driver.startup = ltq_phy_startup;
+	xrx_22f_14_driver.shutdown = genphy_shutdown;
+	xrx_22f_14_driver.name += gd->reloc_off;
+
+	pef7071_driver.config = ltq_phy_config;
+	pef7071_driver.startup = ltq_phy_startup;
+	pef7071_driver.shutdown = genphy_shutdown;
+	pef7071_driver.name += gd->reloc_off;
+
+	xrx_genphy_driver.config = genphy_config;
+	xrx_genphy_driver.startup = ltq_phy_startup;
+	xrx_genphy_driver.shutdown = genphy_shutdown;
+	xrx_genphy_driver.name += gd->reloc_off;
+#endif
+
+	phy_register(&xrx_11g_13_driver);
+	phy_register(&xrx_11g_14_driver);
+	phy_register(&xrx_22f_14_driver);
+	phy_register(&pef7071_driver);
+	phy_register(&xrx_genphy_driver);
+
+	return 0;
+}
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -16,9 +16,10 @@
 #include <command.h>
 #include <miiphy.h>
 #include <phy.h>
-#include <errno.h>
 #include <linux/err.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
 /* Generic PHY support and helper functions */
 
 /**
@@ -440,6 +441,16 @@ static LIST_HEAD(phy_drivers);
 
 int phy_init(void)
 {
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
+	INIT_LIST_HEAD(&phy_drivers);
+
+	genphy_driver.config = genphy_config;
+	genphy_driver.startup = genphy_startup;
+	genphy_driver.shutdown = genphy_shutdown;
+
+	genphy_driver.name += gd->reloc_off;
+#endif
+
 #ifdef CONFIG_PHY_ATHEROS
 	phy_atheros_init();
 #endif
@@ -455,6 +466,9 @@ int phy_init(void)
 #ifdef CONFIG_PHY_ICPLUS
 	phy_icplus_init();
 #endif
+#ifdef CONFIG_PHY_LANTIQ
+	phy_lantiq_init();
+#endif
 #ifdef CONFIG_PHY_LXT
 	phy_lxt_init();
 #endif
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -24,6 +24,7 @@ COBJS-$(CONFIG_SYS_NS16550_SERIAL) += se
 COBJS-$(CONFIG_IMX_SERIAL) += serial_imx.o
 COBJS-$(CONFIG_IXP_SERIAL) += serial_ixp.o
 COBJS-$(CONFIG_KS8695_SERIAL) += serial_ks8695.o
+COBJS-$(CONFIG_LANTIQ_SERIAL) += serial_lantiq.o
 COBJS-$(CONFIG_MAX3100_SERIAL) += serial_max3100.o
 COBJS-$(CONFIG_MXC_UART) += serial_mxc.o
 COBJS-$(CONFIG_PL010_SERIAL) += serial_pl01x.o
--- a/drivers/serial/serial.c
+++ b/drivers/serial/serial.c
@@ -160,6 +160,7 @@ serial_initfunc(sa1100_serial_initialize
 serial_initfunc(sh_serial_initialize);
 serial_initfunc(arm_dcc_initialize);
 serial_initfunc(mxs_auart_initialize);
+serial_initfunc(ltq_serial_initialize);
 
 /**
  * serial_register() - Register serial driver with serial driver core
@@ -253,6 +254,7 @@ void serial_initialize(void)
 	sh_serial_initialize();
 	arm_dcc_initialize();
 	mxs_auart_initialize();
+	ltq_serial_initialize();
 
 	serial_assign(default_serial_console()->name);
 }
--- /dev/null
+++ b/drivers/serial/serial_lantiq.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2010 Thomas Langer <thomas.langer@lantiq.com>
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <serial.h>
+#include <asm/errno.h>
+#include <asm/arch/soc.h>
+#include <asm/lantiq/clk.h>
+#include <asm/lantiq/io.h>
+
+#if CONFIG_CONSOLE_ASC == 0
+#define LTQ_ASC_BASE			LTQ_ASC0_BASE
+#else
+#define LTQ_ASC_BASE			LTQ_ASC1_BASE
+#endif
+
+#define LTQ_ASC_ID_TXFS_SHIFT		24
+#define LTQ_ASC_ID_TXFS_MASK		(0x3F << LTQ_ASC_ID_TXFS_SHIFT)
+#define LTQ_ASC_ID_RXFS_SHIFT		16
+#define LTQ_ASC_ID_RXFS_MASK		(0x3F << LTQ_ASC_ID_RXFS_SHIFT)
+
+#define LTQ_ASC_MCON_R			(1 << 15)
+#define LTQ_ASC_MCON_FDE		(1 << 9)
+
+#define LTQ_ASC_WHBSTATE_SETREN		(1 << 1)
+#define LTQ_ASC_WHBSTATE_CLRREN		(1 << 0)
+
+#define LTQ_ASC_RXFCON_RXFITL_SHIFT	8
+#define LTQ_ASC_RXFCON_RXFITL_MASK	(0x3F << LTQ_ASC_RXFCON_RXFITL_SHIFT)
+#define LTQ_ASC_RXFCON_RXFITL_RXFFLU	(1 << 1)
+#define LTQ_ASC_RXFCON_RXFITL_RXFEN	(1 << 0)
+
+#define LTQ_ASC_TXFCON_TXFITL_SHIFT	8
+#define LTQ_ASC_TXFCON_TXFITL_MASK	(0x3F << LTQ_ASC_TXFCON_TXFITL_SHIFT)
+#define LTQ_ASC_TXFCON_TXFITL_TXFFLU	(1 << 1)
+#define LTQ_ASC_TXFCON_TXFITL_TXFEN	(1 << 0)
+
+#define LTQ_ASC_FSTAT_TXFREE_SHIFT	24
+#define LTQ_ASC_FSTAT_TXFREE_MASK	(0x3F << LTQ_ASC_FSTAT_TXFREE_SHIFT)
+#define LTQ_ASC_FSTAT_RXFREE_SHIFT	16
+#define LTQ_ASC_FSTAT_RXFREE_MASK	(0x3F << LTQ_ASC_FSTAT_RXFREE_SHIFT)
+#define LTQ_ASC_FSTAT_TXFFL_SHIFT	8
+#define LTQ_ASC_FSTAT_TXFFL_MASK	(0x3F << LTQ_ASC_FSTAT_TXFFL_SHIFT)
+#define LTQ_ASC_FSTAT_RXFFL_MASK	0x3F
+
+#ifdef __BIG_ENDIAN
+#define LTQ_ASC_RBUF_OFFSET		3
+#define LTQ_ASC_TBUF_OFFSET		3
+#else
+#define LTQ_ASC_RBUF_OFFSET		0
+#define LTQ_ASC_TBUF_OFFSET		0
+#endif
+
+struct ltq_asc_regs {
+	u32	clc;
+	u32	pisel;
+	u32	id;
+	u32	rsvd0;
+	u32	mcon;
+	u32	state;
+	u32	whbstate;
+	u32	rsvd1;
+	u8	tbuf[4];
+	u8	rbuf[4];
+	u32	rsvd2[2];
+	u32	abcon;
+	u32	abstat;
+	u32	whbabcon;
+	u32	whbabstat;
+	u32	rxfcon;
+	u32	txfcon;
+	u32	fstat;
+	u32	rsvd3;
+	u32	bg;
+	u32	bg_timer;
+	u32	fdv;
+	u32	pmw;
+	u32	modcon;
+	u32	modstat;
+};
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct ltq_asc_regs *ltq_asc_regs =
+	(struct ltq_asc_regs *) CKSEG1ADDR(LTQ_ASC_BASE);
+
+static int ltq_serial_init(void)
+{
+	/* Set clock divider for normal run mode to 1 and enable module */
+	ltq_writel(&ltq_asc_regs->clc, 0x100);
+
+	/* Reset MCON register */
+	ltq_writel(&ltq_asc_regs->mcon, 0);
+
+	/* Use Port A as receiver input */
+	ltq_writel(&ltq_asc_regs->pisel, 0);
+
+	/* Enable and flush RX/TX FIFOs */
+	ltq_setbits(&ltq_asc_regs->rxfcon,
+		LTQ_ASC_RXFCON_RXFITL_RXFFLU | LTQ_ASC_RXFCON_RXFITL_RXFEN);
+	ltq_setbits(&ltq_asc_regs->txfcon,
+		LTQ_ASC_TXFCON_TXFITL_TXFFLU | LTQ_ASC_TXFCON_TXFITL_TXFEN);
+
+	serial_setbrg();
+
+	/* Disable error flags, enable receiver */
+	ltq_writel(&ltq_asc_regs->whbstate, LTQ_ASC_WHBSTATE_SETREN);
+
+	return 0;
+}
+
+/*
+ *             fdv       asc_clk
+ * Baudrate = ----- * -------------
+ *             512    16 * (bg + 1)
+ */
+static void ltq_serial_calc_br_fdv(unsigned long asc_clk,
+					unsigned long baudrate, u16 *fdv,
+					u16 *bg)
+{
+	const u32 c = asc_clk / (16 * 512);
+	u32 diff1, diff2;
+	u32 bg_calc, br_calc, i;
+
+	diff1 = baudrate;
+	for (i = 512; i > 0; i--) {
+		/* Calc bg for current fdv value */
+		bg_calc = i * c / baudrate;
+
+		/* Impossible baudrate */
+		if (!bg_calc)
+			return;
+
+		/*
+		 * Calc diff to target baudrate dependent on current
+		 * bg and fdv values
+		 */
+		br_calc = i * c / bg_calc;
+		if (br_calc > baudrate)
+			diff2 = br_calc - baudrate;
+		else
+			diff2 = baudrate - br_calc;
+
+		/* Perfect values found */
+		if (diff2 == 0) {
+			*fdv = i;
+			*bg = bg_calc - 1;
+			return;
+		}
+
+		if (diff2 < diff1) {
+			*fdv = i;
+			*bg = bg_calc - 1;
+			diff1 = diff2;
+		}
+	}
+}
+
+static void ltq_serial_setbrg(void)
+{
+	unsigned long asc_clk, baudrate;
+	u16 bg = 0;
+	u16 fdv = 511;
+
+	/* ASC clock is same as FPI clock with CLC.RMS = 1 */
+	asc_clk = ltq_get_bus_clock();
+	baudrate = gd->baudrate;
+
+	/* Calculate FDV and BG values */
+	ltq_serial_calc_br_fdv(asc_clk, baudrate, &fdv, &bg);
+
+	/* Disable baudrate generator */
+	ltq_clrbits(&ltq_asc_regs->mcon, LTQ_ASC_MCON_R);
+
+	/* Enable fractional divider */
+	ltq_setbits(&ltq_asc_regs->mcon, LTQ_ASC_MCON_FDE);
+
+	/* Set fdv and bg values */
+	ltq_writel(&ltq_asc_regs->fdv, fdv);
+	ltq_writel(&ltq_asc_regs->bg, bg);
+
+	/* Enable baudrate generator */
+	ltq_setbits(&ltq_asc_regs->mcon, LTQ_ASC_MCON_R);
+}
+
+static unsigned int ltq_serial_tx_free(void)
+{
+	unsigned int txfree;
+
+	txfree = (ltq_readl(&ltq_asc_regs->fstat) &
+			LTQ_ASC_FSTAT_TXFREE_MASK) >>
+			LTQ_ASC_FSTAT_TXFREE_SHIFT;
+
+	return txfree;
+}
+
+static unsigned int ltq_serial_rx_fill(void)
+{
+	unsigned int rxffl;
+
+	rxffl = ltq_readl(&ltq_asc_regs->fstat) & LTQ_ASC_FSTAT_RXFFL_MASK;
+
+	return rxffl;
+}
+
+static void ltq_serial_tx(const char c)
+{
+	ltq_writeb(&ltq_asc_regs->tbuf[LTQ_ASC_TBUF_OFFSET], c);
+}
+
+static u8 ltq_serial_rx(void)
+{
+	return ltq_readb(&ltq_asc_regs->rbuf[LTQ_ASC_RBUF_OFFSET]);
+}
+
+static void ltq_serial_putc(const char c)
+{
+	if (c == '\n')
+		ltq_serial_putc('\r');
+
+	while (!ltq_serial_tx_free())
+		;
+
+	ltq_serial_tx(c);
+}
+
+static int ltq_serial_getc(void)
+{
+	while (!ltq_serial_rx_fill())
+		;
+
+	return ltq_serial_rx();
+}
+
+static int ltq_serial_tstc(void)
+{
+	return (0 != ltq_serial_rx_fill());
+}
+
+static struct serial_device ltq_serial_drv = {
+	.name	= "ltq_serial",
+	.start	= ltq_serial_init,
+	.stop	= NULL,
+	.setbrg	= ltq_serial_setbrg,
+	.putc	= ltq_serial_putc,
+	.puts	= default_serial_puts,
+	.getc	= ltq_serial_getc,
+	.tstc	= ltq_serial_tstc,
+};
+
+void ltq_serial_initialize(void)
+{
+	serial_register(&ltq_serial_drv);
+}
+
+__weak struct serial_device *default_serial_console(void)
+{
+	return &ltq_serial_drv;
+}
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -25,6 +25,7 @@ COBJS-$(CONFIG_DAVINCI_SPI) += davinci_s
 COBJS-$(CONFIG_EXYNOS_SPI) += exynos_spi.o
 COBJS-$(CONFIG_ICH_SPI) +=  ich.o
 COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
+COBJS-$(CONFIG_LANTIQ_SPI) += lantiq_spi.o
 COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
 COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
 COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o
--- /dev/null
+++ b/drivers/spi/lantiq_spi.c
@@ -0,0 +1,666 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <spi.h>
+#include <malloc.h>
+#include <watchdog.h>
+#include <asm/gpio.h>
+#include <asm/lantiq/io.h>
+#include <asm/lantiq/clk.h>
+#include <asm/lantiq/pm.h>
+#include <asm/arch/soc.h>
+
+#define LTQ_SPI_CLC_RMC_SHIFT		8
+#define LTQ_SPI_CLC_RMC_MASK		(0xFF << LTQ_SPI_CLC_RMC_SHIFT)
+#define LTQ_SPI_CLC_DISS		(1 << 1)
+#define LTQ_SPI_CLC_DISR		1
+
+#define LTQ_SPI_ID_TXFS_SHIFT		24
+#define LTQ_SPI_ID_TXFS_MASK		(0x3F << LTQ_SPI_ID_TXFS_SHIFT)
+#define LTQ_SPI_ID_RXFS_SHIFT		16
+#define LTQ_SPI_ID_RXFS_MASK		(0x3F << LTQ_SPI_ID_RXFS_SHIFT)
+
+#define LTQ_SPI_CON_ENBV		(1 << 22)
+#define LTQ_SPI_CON_BM_SHIFT		16
+#define LTQ_SPI_CON_BM_MASK		(0x1F << LTQ_SPI_CON_BM_SHIFT)
+#define LTQ_SPI_CON_IDLE		(1 << 23)
+#define LTQ_SPI_CON_RUEN		(1 << 12)
+#define LTQ_SPI_CON_AEN			(1 << 10)
+#define LTQ_SPI_CON_REN			(1 << 9)
+#define LTQ_SPI_CON_TEN			(1 << 8)
+#define LTQ_SPI_CON_LB			(1 << 7)
+#define LTQ_SPI_CON_PO			(1 << 6)
+#define LTQ_SPI_CON_PH			(1 << 5)
+#define LTQ_SPI_CON_HB			(1 << 4)
+#define LTQ_SPI_CON_RXOFF		(1 << 1)
+#define LTQ_SPI_CON_TXOFF		1
+
+#define LTQ_SPI_STAT_RXBV_SHIFT		28
+#define LTQ_SPI_STAT_RXBV_MASK		(0x7 << LTQ_SPI_STAT_RXBV_SHIFT)
+#define LTQ_SPI_STAT_BSY		(1 << 13)
+
+#define LTQ_SPI_WHBSTATE_SETMS		(1 << 3)
+#define LTQ_SPI_WHBSTATE_CLRMS		(1 << 2)
+#define LTQ_SPI_WHBSTATE_SETEN		(1 << 1)
+#define LTQ_SPI_WHBSTATE_CLREN		1
+#define LTQ_SPI_WHBSTATE_CLR_ERRORS	0x0F50
+
+#define LTQ_SPI_TXFCON_TXFLU		(1 << 1)
+#define LTQ_SPI_TXFCON_TXFEN		1
+
+#define LTQ_SPI_RXFCON_RXFLU		(1 << 1)
+#define LTQ_SPI_RXFCON_RXFEN		1
+
+#define LTQ_SPI_FSTAT_RXFFL_MASK	0x3f
+#define LTQ_SPI_FSTAT_TXFFL_SHIFT	8
+#define LTQ_SPI_FSTAT_TXFFL_MASK	(0x3f << LTQ_SPI_FSTAT_TXFFL_SHIFT)
+
+#define LTQ_SPI_RXREQ_RXCNT_MASK	0xFFFF
+#define LTQ_SPI_RXCNT_TODO_MASK		0xFFFF
+
+#define LTQ_SPI_GPIO_DIN		16
+#define LTQ_SPI_GPIO_DOUT		17
+#define LTQ_SPI_GPIO_CLK		18
+
+struct ltq_spi_regs {
+	__be32	clc;		/* Clock control */
+	__be32	pisel;		/* Port input select */
+	__be32	id;		/* Identification */
+	__be32	rsvd0;
+	__be32	con;		/* Control */
+	__be32	stat;		/* Status */
+	__be32	whbstate;	/* Write HW modified state */
+	__be32	rsvd1;
+	__be32	tb;		/* Transmit buffer */
+	__be32	rb;		/* Receive buffer */
+	__be32	rsvd2[2];
+	__be32	rxfcon;		/* Recevie FIFO control */
+	__be32	txfcon;		/* Transmit FIFO control */
+	__be32	fstat;		/* FIFO status */
+	__be32	rsvd3;
+	__be32	brt;		/* Baudrate timer */
+	__be32	brstat;		/* Baudrate timer status */
+	__be32	rsvd4[6];
+	__be32	sfcon;		/* Serial frame control */
+	__be32	sfstat;		/* Serial frame status */
+	__be32	rsvd5[2];
+	__be32	gpocon;		/* General purpose output control */
+	__be32	gpostat;	/* General purpose output status */
+	__be32	fgpo;		/* Force general purpose output */
+	__be32	rsvd6;
+	__be32	rxreq;		/* Receive request */
+	__be32	rxcnt;		/* Receive count */
+	__be32	rsvd7[25];
+	__be32	dmacon;		/* DMA control */
+	__be32	rsvd8;
+	__be32	irnen;		/* Interrupt node enable */
+	__be32	irnicr;		/* Interrupt node interrupt capture */
+	__be32	irncr;		/* Interrupt node control */
+};
+
+struct ltq_spi_drv_data {
+	struct ltq_spi_regs __iomem *regs;
+
+	struct spi_slave slave;
+	unsigned int max_hz;
+	unsigned int mode;
+	unsigned int tx_todo;
+	unsigned int rx_todo;
+	unsigned int rx_req;
+	unsigned int bits_per_word;
+	unsigned int speed_hz;
+	const u8 *tx;
+	u8 *rx;
+	int status;
+};
+
+static struct ltq_spi_drv_data *to_ltq_spi_slave(struct spi_slave *slave)
+{
+	return container_of(slave, struct ltq_spi_drv_data, slave);
+}
+
+#ifdef CONFIG_SPL_BUILD
+/*
+ * We do not have or want malloc in a SPI flash SPL.
+ * Neither we have to support multiple SPI slaves. Thus we put the
+ * SPI slave context in BSS for SPL builds.
+ */
+static struct ltq_spi_drv_data ltq_spi_slave;
+
+static struct ltq_spi_drv_data *ltq_spi_slave_alloc(unsigned int bus,
+							unsigned int cs)
+{
+	ltq_spi_slave.slave.bus = bus;
+	ltq_spi_slave.slave.cs = cs;
+
+	return &ltq_spi_slave;
+}
+
+static void ltq_spi_slave_free(struct spi_slave *slave)
+{
+}
+#else
+static struct ltq_spi_drv_data *ltq_spi_slave_alloc(unsigned int bus,
+							unsigned int cs)
+{
+	return spi_alloc_slave(struct ltq_spi_drv_data, bus, cs);
+}
+
+static void ltq_spi_slave_free(struct spi_slave *slave)
+{
+	struct ltq_spi_drv_data *drv;
+
+	if (slave) {
+		drv = to_ltq_spi_slave(slave);
+		free(drv);
+	}
+}
+#endif
+
+static unsigned int tx_fifo_size(struct ltq_spi_drv_data *drv)
+{
+	u32 id = ltq_readl(&drv->regs->id);
+
+	return (id & LTQ_SPI_ID_TXFS_MASK) >> LTQ_SPI_ID_TXFS_SHIFT;
+}
+
+static unsigned int rx_fifo_size(struct ltq_spi_drv_data *drv)
+{
+	u32 id = ltq_readl(&drv->regs->id);
+
+	return (id & LTQ_SPI_ID_RXFS_MASK) >> LTQ_SPI_ID_RXFS_SHIFT;
+}
+
+static unsigned int tx_fifo_level(struct ltq_spi_drv_data *drv)
+{
+	u32 fstat = ltq_readl(&drv->regs->fstat);
+
+	return (fstat & LTQ_SPI_FSTAT_TXFFL_MASK) >> LTQ_SPI_FSTAT_TXFFL_SHIFT;
+}
+
+static unsigned int rx_fifo_level(struct ltq_spi_drv_data *drv)
+{
+	u32 fstat = ltq_readl(&drv->regs->fstat);
+
+	return fstat & LTQ_SPI_FSTAT_RXFFL_MASK;
+}
+
+static unsigned int tx_fifo_free(struct ltq_spi_drv_data *drv)
+{
+	return tx_fifo_size(drv) - tx_fifo_level(drv);
+}
+
+static void hw_power_on(struct ltq_spi_drv_data *drv)
+{
+	u32 clc;
+
+	/* Power-up mdule */
+	ltq_pm_enable(LTQ_PM_SPI);
+
+	/*
+	 * Set clock divider for run mode to 1 to
+	 * run at same frequency as FPI bus
+	 */
+	clc = (1 << LTQ_SPI_CLC_RMC_SHIFT);
+	ltq_writel(&drv->regs->clc, clc);
+}
+
+static void hw_reset_fifos(struct ltq_spi_drv_data *drv)
+{
+	u32 val;
+
+	val = LTQ_SPI_TXFCON_TXFEN | LTQ_SPI_TXFCON_TXFLU;
+	ltq_writel(&drv->regs->txfcon, val);
+
+	val = LTQ_SPI_RXFCON_RXFEN | LTQ_SPI_RXFCON_RXFLU;
+	ltq_writel(&drv->regs->rxfcon, val);
+}
+
+static int hw_is_busy(struct ltq_spi_drv_data *drv)
+{
+	u32 stat = ltq_readl(&drv->regs->stat);
+
+	return stat & LTQ_SPI_STAT_BSY;
+}
+
+static void hw_enter_config_mode(struct ltq_spi_drv_data *drv)
+{
+	ltq_writel(&drv->regs->whbstate, LTQ_SPI_WHBSTATE_CLREN);
+}
+
+static void hw_enter_active_mode(struct ltq_spi_drv_data *drv)
+{
+	ltq_writel(&drv->regs->whbstate, LTQ_SPI_WHBSTATE_SETEN);
+}
+
+static void hw_setup_speed_hz(struct ltq_spi_drv_data *drv,
+				unsigned int max_speed_hz)
+{
+	unsigned int spi_hz, speed_hz, brt;
+
+	/*
+	 * SPI module clock is derived from FPI bus clock dependent on
+	 * divider value in CLC.RMS which is always set to 1.
+	 *
+	 *                 f_SPI
+	 * baudrate = --------------
+	 *             2 * (BR + 1)
+	 */
+	spi_hz = ltq_get_bus_clock() / 2;
+
+	/* TODO: optimize baudrate calculation */
+	for (brt = 0; brt < 0xFFFF; brt++) {
+		speed_hz = spi_hz / (brt + 1);
+		if (speed_hz <= max_speed_hz)
+			break;
+	}
+
+	ltq_writel(&drv->regs->brt, brt);
+}
+
+static void hw_setup_bits_per_word(struct ltq_spi_drv_data *drv,
+					unsigned int bits_per_word)
+{
+	u32 bm;
+
+	/* CON.BM value = bits_per_word - 1 */
+	bm = (bits_per_word - 1) << LTQ_SPI_CON_BM_SHIFT;
+
+	ltq_clrsetbits(&drv->regs->con, LTQ_SPI_CON_BM_MASK, bm);
+}
+
+static void hw_setup_clock_mode(struct ltq_spi_drv_data *drv, unsigned int mode)
+{
+	u32 con_set = 0, con_clr = 0;
+
+	/*
+	 * SPI mode mapping in CON register:
+	 * Mode CPOL CPHA CON.PO CON.PH
+	 *  0    0    0      0      1
+	 *  1    0    1      0      0
+	 *  2    1    0      1      1
+	 *  3    1    1      1      0
+	 */
+	if (mode & SPI_CPHA)
+		con_clr |= LTQ_SPI_CON_PH;
+	else
+		con_set |= LTQ_SPI_CON_PH;
+
+	if (mode & SPI_CPOL)
+		con_set |= LTQ_SPI_CON_PO | LTQ_SPI_CON_IDLE;
+	else
+		con_clr |= LTQ_SPI_CON_PO | LTQ_SPI_CON_IDLE;
+
+	/* Set heading control */
+	if (mode & SPI_LSB_FIRST)
+		con_clr |= LTQ_SPI_CON_HB;
+	else
+		con_set |= LTQ_SPI_CON_HB;
+
+	/* Set loopback mode */
+	if (mode & SPI_LOOP)
+		con_set |= LTQ_SPI_CON_LB;
+	else
+		con_clr |= LTQ_SPI_CON_LB;
+
+	ltq_clrsetbits(&drv->regs->con, con_clr, con_set);
+}
+
+static void hw_set_rxtx(struct ltq_spi_drv_data *drv)
+{
+	u32 con;
+
+	/* Configure transmitter and receiver */
+	con = ltq_readl(&drv->regs->con);
+	if (drv->tx)
+		con &= ~LTQ_SPI_CON_TXOFF;
+	else
+		con |= LTQ_SPI_CON_TXOFF;
+
+	if (drv->rx)
+		con &= ~LTQ_SPI_CON_RXOFF;
+	else
+		con |= LTQ_SPI_CON_RXOFF;
+
+	ltq_writel(&drv->regs->con, con);
+}
+
+static void hw_init(struct ltq_spi_drv_data *drv)
+{
+	hw_power_on(drv);
+
+	/* Put controller into config mode */
+	hw_enter_config_mode(drv);
+
+	/* Disable all interrupts */
+	ltq_writel(&drv->regs->irnen, 0);
+
+	/* Clear error flags */
+	ltq_clrsetbits(&drv->regs->whbstate, 0, LTQ_SPI_WHBSTATE_CLR_ERRORS);
+
+	/* Enable error checking, disable TX/RX */
+	ltq_writel(&drv->regs->con, LTQ_SPI_CON_RUEN | LTQ_SPI_CON_AEN |
+			LTQ_SPI_CON_TEN | LTQ_SPI_CON_REN | LTQ_SPI_CON_TXOFF |
+			LTQ_SPI_CON_RXOFF);
+
+	/* Setup default SPI mode */
+	drv->bits_per_word = 8;
+	drv->speed_hz = 0;
+	hw_setup_bits_per_word(drv, drv->bits_per_word);
+	hw_setup_clock_mode(drv, SPI_MODE_0);
+
+	/* Enable master mode and clear error flags */
+	ltq_writel(&drv->regs->whbstate, LTQ_SPI_WHBSTATE_SETMS |
+			LTQ_SPI_WHBSTATE_CLR_ERRORS);
+
+	/* Reset GPIO/CS registers */
+	ltq_writel(&drv->regs->gpocon, 0);
+	ltq_writel(&drv->regs->fgpo, 0xFF00);
+
+	/* Enable and flush FIFOs */
+	hw_reset_fifos(drv);
+
+	/* SPI/DIN input */
+	gpio_set_altfunc(16, GPIO_ALTSEL_SET, GPIO_ALTSEL_CLR, GPIO_DIR_IN);
+	/* SPI/DOUT output */
+	gpio_set_altfunc(17, GPIO_ALTSEL_SET, GPIO_ALTSEL_CLR, GPIO_DIR_OUT);
+	/* SPI/CLK output */
+	gpio_set_altfunc(18, GPIO_ALTSEL_SET, GPIO_ALTSEL_CLR, GPIO_DIR_OUT);
+}
+
+static void tx_fifo_write(struct ltq_spi_drv_data *drv)
+{
+	const u8 *tx8;
+	const u16 *tx16;
+	const u32 *tx32;
+	u32 data;
+	unsigned int tx_free = tx_fifo_free(drv);
+
+	while (drv->tx_todo && tx_free) {
+		switch (drv->bits_per_word) {
+		case 8:
+			tx8 = drv->tx;
+			data = *tx8;
+			drv->tx_todo--;
+			drv->tx++;
+			break;
+		case 16:
+			tx16 = (u16 *) drv->tx;
+			data = *tx16;
+			drv->tx_todo -= 2;
+			drv->tx += 2;
+			break;
+		case 32:
+			tx32 = (u32 *) drv->tx;
+			data = *tx32;
+			drv->tx_todo -= 4;
+			drv->tx += 4;
+			break;
+		default:
+			return;
+		}
+
+		ltq_writel(&drv->regs->tb, data);
+		tx_free--;
+	}
+}
+
+static void rx_fifo_read_full_duplex(struct ltq_spi_drv_data *drv)
+{
+	u8 *rx8;
+	u16 *rx16;
+	u32 *rx32;
+	u32 data;
+	unsigned int rx_fill = rx_fifo_level(drv);
+
+	while (rx_fill) {
+		data = ltq_readl(&drv->regs->rb);
+
+		switch (drv->bits_per_word) {
+		case 8:
+			rx8 = drv->rx;
+			*rx8 = data;
+			drv->rx_todo--;
+			drv->rx++;
+			break;
+		case 16:
+			rx16 = (u16 *) drv->rx;
+			*rx16 = data;
+			drv->rx_todo -= 2;
+			drv->rx += 2;
+			break;
+		case 32:
+			rx32 = (u32 *) drv->rx;
+			*rx32 = data;
+			drv->rx_todo -= 4;
+			drv->rx += 4;
+			break;
+		default:
+			return;
+		}
+
+		rx_fill--;
+	}
+}
+
+static void rx_fifo_read_half_duplex(struct ltq_spi_drv_data *drv)
+{
+	u32 data, *rx32;
+	u8 *rx8;
+	unsigned int rxbv, shift;
+	unsigned int rx_fill = rx_fifo_level(drv);
+
+	/*
+	 * In RX-only mode the bits per word value is ignored by HW. A value
+	 * of 32 is used instead. Thus all 4 bytes per FIFO must be read.
+	 * If remaining RX bytes are less than 4, the FIFO must be read
+	 * differently. The amount of received and valid bytes is indicated
+	 * by STAT.RXBV register value.
+	 */
+	while (rx_fill) {
+		if (drv->rx_todo < 4) {
+			rxbv = (ltq_readl(&drv->regs->stat) &
+				LTQ_SPI_STAT_RXBV_MASK) >>
+				LTQ_SPI_STAT_RXBV_SHIFT;
+			data = ltq_readl(&drv->regs->rb);
+
+			shift = (rxbv - 1) * 8;
+			rx8 = drv->rx;
+
+			while (rxbv) {
+				*rx8++ = (data >> shift) & 0xFF;
+				rxbv--;
+				shift -= 8;
+				drv->rx_todo--;
+				drv->rx++;
+
+				if (drv->rx_req)
+					drv->rx_req --;
+			}
+		} else {
+			data = ltq_readl(&drv->regs->rb);
+			rx32 = (u32 *) drv->rx;
+
+			*rx32++ = data;
+			drv->rx_todo -= 4;
+			drv->rx += 4;
+
+			if (drv->rx_req >= 4)
+				drv->rx_req -= 4;
+		}
+		rx_fill--;
+	}
+}
+
+static void rx_request(struct ltq_spi_drv_data *drv)
+{
+	unsigned int rxreq, rxreq_max;
+
+	if (drv->rx_req)
+		return;
+
+	/*
+	 * To avoid receive overflows at high clocks it is better to request
+	 * only the amount of bytes that fits into all FIFOs. This value
+	 * depends on the FIFO size implemented in hardware.
+	 */
+	rxreq = drv->rx_todo;
+	rxreq_max = rx_fifo_size(drv) * 4;
+	if (rxreq > rxreq_max)
+		rxreq = rxreq_max;
+
+	drv->rx_req = rxreq;
+	ltq_writel(&drv->regs->rxreq, rxreq);
+}
+
+void spi_init(void)
+{
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+				  unsigned int max_hz, unsigned int mode)
+{
+	struct ltq_spi_drv_data *drv;
+
+	if (!spi_cs_is_valid(bus, cs))
+		return NULL;
+
+	drv = ltq_spi_slave_alloc(bus, cs);
+	if (!drv)
+		return NULL;
+
+	drv->regs = (struct ltq_spi_regs *) CKSEG1ADDR(LTQ_SPI_BASE);
+
+	hw_init(drv);
+
+	drv->max_hz = max_hz;
+	drv->mode = mode;
+
+	return &drv->slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+	ltq_spi_slave_free(slave);
+}
+
+static int ltq_spi_wait_ready(struct ltq_spi_drv_data *drv)
+{
+	const unsigned long timeout = 20000;
+	unsigned long timebase;
+
+	timebase = get_timer(0);
+
+	do {
+		WATCHDOG_RESET();
+
+		if (!hw_is_busy(drv))
+			return 0;
+	} while (get_timer(timebase) < timeout);
+
+	return 1;
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+	struct ltq_spi_drv_data *drv = to_ltq_spi_slave(slave);
+	int ret;
+
+	ret = ltq_spi_wait_ready(drv);
+	if (ret) {
+		debug("cannot claim bus\n");
+		return ret;
+	}
+
+	hw_enter_config_mode(drv);
+	hw_setup_clock_mode(drv, drv->mode);
+	hw_setup_speed_hz(drv, drv->max_hz);
+	hw_setup_bits_per_word(drv, drv->bits_per_word);
+	hw_enter_active_mode(drv);
+
+	return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+	struct ltq_spi_drv_data *drv = to_ltq_spi_slave(slave);
+
+	hw_enter_config_mode(drv);
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+		const void *dout, void *din, unsigned long flags)
+{
+
+	struct ltq_spi_drv_data *drv = to_ltq_spi_slave(slave);
+	int ret = 0;
+
+	if (bitlen % 8)
+		return 1;
+
+	if (!bitlen) {
+		ret = 0;
+		goto done;
+	}
+
+	if (flags & SPI_XFER_BEGIN)
+		spi_cs_activate(slave);
+
+	drv->tx = dout;
+	drv->tx_todo = 0;
+	drv->rx = din;
+	drv->rx_todo = 0;
+	hw_set_rxtx(drv);
+
+	if (drv->tx) {
+		drv->tx_todo = bitlen / 8;
+
+		tx_fifo_write(drv);
+	}
+
+	if (drv->rx) {
+		drv->rx_todo = bitlen / 8;
+
+		if (!drv->tx)
+			rx_request(drv);
+	}
+
+	for (;;) {
+		if (drv->tx) {
+			if (drv->rx && drv->rx_todo)
+				rx_fifo_read_full_duplex(drv);
+
+			if (drv->tx_todo)
+				tx_fifo_write(drv);
+			else
+				goto done;
+		} else if (drv->rx) {
+			if (drv->rx_todo) {
+				rx_fifo_read_half_duplex(drv);
+
+				if (drv->rx_todo)
+					rx_request(drv);
+				else
+					goto done;
+			} else {
+				goto done;
+			}
+		}
+	}
+
+done:
+	ret = ltq_spi_wait_ready(drv);
+
+	drv->rx = NULL;
+	drv->tx = NULL;
+	hw_set_rxtx(drv);
+
+	if (flags & SPI_XFER_END)
+		spi_cs_deactivate(slave);
+
+	return ret;
+}
--- a/include/phy.h
+++ b/include/phy.h
@@ -214,6 +214,7 @@ int phy_atheros_init(void);
 int phy_broadcom_init(void);
 int phy_davicom_init(void);
 int phy_et1011c_init(void);
+int phy_lantiq_init(void);
 int phy_lxt_init(void);
 int phy_marvell_init(void);
 int phy_micrel_init(void);
--- a/spl/Makefile
+++ b/spl/Makefile
@@ -100,6 +100,8 @@ LIBS-$(CONFIG_SPL_USBETH_SUPPORT) += dri
 LIBS-$(CONFIG_SPL_MUSB_NEW_SUPPORT) += drivers/usb/musb-new/libusb_musb-new.o
 LIBS-$(CONFIG_SPL_USBETH_SUPPORT) += drivers/usb/gadget/libusb_gadget.o
 LIBS-$(CONFIG_SPL_WATCHDOG_SUPPORT) += drivers/watchdog/libwatchdog.o
+LIBS-$(CONFIG_SPL_LZMA_SUPPORT) += lib/lzma/liblzma.o
+LIBS-$(CONFIG_SPL_LZO_SUPPORT) += lib/lzo/liblzo.o
 
 ifneq ($(CONFIG_OMAP_COMMON),)
 LIBS-y += $(CPUDIR)/omap-common/libomap-common.o
--- a/tools/.gitignore
+++ b/tools/.gitignore
@@ -2,6 +2,7 @@
 /envcrc
 /gen_eth_addr
 /img2srec
+/ltq-boot-image
 /kwboot
 /mkenvimage
 /mkimage
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -49,6 +49,7 @@ BIN_FILES-$(CONFIG_VIDEO_LOGO) += bmp_lo
 BIN_FILES-$(CONFIG_BUILD_ENVCRC) += envcrc$(SFX)
 BIN_FILES-$(CONFIG_CMD_NET) += gen_eth_addr$(SFX)
 BIN_FILES-$(CONFIG_CMD_LOADS) += img2srec$(SFX)
+BIN_FILES-$(CONFIG_SOC_LANTIQ) += ltq-boot-image$(SFX)
 BIN_FILES-$(CONFIG_XWAY_SWAP_BYTES) += xway-swap-bytes$(SFX)
 BIN_FILES-y += mkenvimage$(SFX)
 BIN_FILES-y += mkimage$(SFX)
@@ -95,6 +96,7 @@ OBJ_FILES-$(CONFIG_MX28) += mxsboot.o
 OBJ_FILES-$(CONFIG_NETCONSOLE) += ncb.o
 OBJ_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1.o
 OBJ_FILES-$(CONFIG_SMDK5250) += mkexynosspl.o
+OBJ_FILES-$(CONFIG_SOC_LANTIQ) += ltq-boot-image.o
 OBJ_FILES-$(CONFIG_VIDEO_LOGO) += bmp_logo.o
 OBJ_FILES-$(CONFIG_XWAY_SWAP_BYTES) += xway-swap-bytes.o
 
@@ -195,6 +197,10 @@ $(obj)img2srec$(SFX):	$(obj)img2srec.o
 	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^
 	$(HOSTSTRIP) $@
 
+$(obj)ltq-boot-image$(SFX):	$(obj)ltq-boot-image.o
+	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^
+	$(HOSTSTRIP) $@
+
 $(obj)xway-swap-bytes$(SFX):	$(obj)xway-swap-bytes.o
 	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^
 	$(HOSTSTRIP) $@
--- /dev/null
+++ b/tools/ltq-boot-image.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <compiler.h>
+#include <sys/stat.h>
+
+enum image_types {
+	IMAGE_NONE,
+	IMAGE_SFSPL
+};
+
+/* Lantiq non-volatile bootstrap command IDs */
+enum nvb_cmd_ids {
+	NVB_CMD_DEBUG	= 0x11,
+	NVB_CMD_REGCFG	= 0x22,
+	NVB_CMD_IDWNLD	= 0x33,
+	NVB_CMD_CDWNLD	= 0x44,
+	NVB_CMD_DWNLD	= 0x55,
+	NVB_CMD_IFCFG	= 0x66,
+	NVB_CMD_START	= 0x77
+};
+
+/* Lantiq non-volatile bootstrap command flags */
+enum nvb_cmd_flags {
+	NVB_FLAG_START	= 1,
+	NVB_FLAG_DEC	= (1 << 1),
+	NVB_FLAG_DBG	= (1 << 2),
+	NVB_FLAG_SDBG	= (1 << 3),
+	NVB_FLAG_CFG0	= (1 << 4),
+	NVB_FLAG_CFG1	= (1 << 5),
+	NVB_FLAG_CFG2	= (1 << 6),
+	NVB_FLAG_RST	= (1 << 7)
+};
+
+struct args {
+	enum image_types type;
+	__u32		entry_addr;
+	const char	*uboot_bin;
+	const char	*spl_bin;
+	const char	*out_bin;
+};
+
+static void usage_msg(const char *name)
+{
+	fprintf(stderr, "%s: [-h] -t type -e entry-addr -u uboot-bin [-s spl-bin] -o out-bin\n",
+		name);
+	fprintf(stderr, " Image types:\n"
+			"  sfspl  - SPL + [compressed] U-Boot for SPI flash\n");
+}
+
+static enum image_types parse_image_type(const char *type)
+{
+	if (!type)
+		return IMAGE_NONE;
+
+	if (!strncmp(type, "sfspl", 6))
+		return IMAGE_SFSPL;
+
+	return IMAGE_NONE;
+}
+
+static int parse_args(int argc, char *argv[], struct args *arg)
+{
+	int opt;
+
+	memset(arg, 0, sizeof(*arg));
+
+	while ((opt = getopt(argc, argv, "ht:e:u:s:o:")) != -1) {
+		switch (opt) {
+		case 'h':
+			usage_msg(argv[0]);
+			return 1;
+		case 't':
+			arg->type = parse_image_type(optarg);
+			break;
+		case 'e':
+			arg->entry_addr = strtoul(optarg, NULL, 16);
+			break;
+		case 'u':
+			arg->uboot_bin = optarg;
+			break;
+		case 's':
+			arg->spl_bin = optarg;
+			break;
+		case 'o':
+			arg->out_bin = optarg;
+			break;
+		default:
+			fprintf(stderr, "Invalid option -%c\n", opt);
+			goto parse_error;
+		}
+	}
+
+	if (arg->type == IMAGE_NONE) {
+		fprintf(stderr, "Invalid image type\n");
+		goto parse_error;
+	}
+
+	if (!arg->uboot_bin) {
+		fprintf(stderr, "Missing U-Boot binary\n");
+		goto parse_error;
+	}
+
+	if (!arg->out_bin) {
+		fprintf(stderr, "Missing output binary\n");
+		goto parse_error;
+	}
+
+	if (arg->type == IMAGE_SFSPL && !arg->spl_bin) {
+		fprintf(stderr, "Missing SPL binary\n");
+		goto parse_error;
+	}
+
+	return 0;
+
+parse_error:
+	usage_msg(argv[0]);
+	return -1;
+}
+
+static __u32 build_nvb_command(unsigned cmdid, unsigned cmdflags)
+{
+	__u32 cmd;
+	__u16 tag;
+
+	tag = (cmdid << 8) | cmdflags;
+	cmd = (tag << 16) | (0xFFFF - tag);
+
+	return cpu_to_be32(cmd);
+}
+
+static int write_header(int fd, const void *hdr, size_t size)
+{
+	ssize_t n;
+
+	n = write(fd, hdr, size);
+	if (n != size) {
+		fprintf(stderr, "Cannot write header: %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int write_nvb_dwnld_header(int fd, size_t size, __u32 addr)
+{
+	__u32 hdr[3];
+
+	hdr[0] = build_nvb_command(NVB_CMD_DWNLD, NVB_FLAG_START |
+					NVB_FLAG_SDBG);
+	hdr[1] = cpu_to_be32(size + 4);
+	hdr[2] = cpu_to_be32(addr);
+
+	return write_header(fd, hdr, sizeof(hdr));
+}
+
+static int write_nvb_start_header(int fd, __u32 addr)
+{
+	__u32 hdr[3];
+
+	hdr[0] = build_nvb_command(NVB_CMD_START, NVB_FLAG_SDBG);
+	hdr[1] = cpu_to_be32(4);
+	hdr[2] = cpu_to_be32(addr);
+
+	return write_header(fd, hdr, sizeof(hdr));
+}
+
+static int open_input_bin(const char *name, void **ptr, size_t *size)
+{
+	struct stat sbuf;
+	int ret, fd;
+
+	fd = open(name, O_RDONLY | O_BINARY);
+	if (0 > fd) {
+		fprintf(stderr, "Cannot open %s: %s\n", name,
+			strerror(errno));
+		return -1;
+	}
+
+	ret = fstat(fd, &sbuf);
+	if (0 > ret) {
+		fprintf(stderr, "Cannot fstat %s: %s\n", name,
+			strerror(errno));
+		return -1;
+	}
+
+	*ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+	if (*ptr == MAP_FAILED) {
+		fprintf(stderr, "Cannot mmap %s: %s\n", name,
+			strerror(errno));
+		return -1;
+	}
+
+	*size = sbuf.st_size;
+
+	return fd;
+}
+
+static void close_input_bin(int fd, void *ptr, size_t size)
+{
+	munmap(ptr, size);
+	close(fd);
+}
+
+static int copy_bin(int fd, void *ptr, size_t size)
+{
+	ssize_t n;
+
+	n = write(fd, ptr, size);
+	if (n != size) {
+		fprintf(stderr, "Cannot copy binary: %s\n", strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int open_output_bin(const char *name)
+{
+	int fd;
+
+	fd = open(name, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0666);
+	if (0 > fd) {
+		fprintf(stderr, "Cannot open %s: %s\n", name,
+			strerror(errno));
+		return -1;
+	}
+
+	return fd;
+}
+
+static int create_sfspl(const struct args *arg)
+{
+	int out_fd, uboot_fd, spl_fd, ret;
+	void *uboot_ptr, *spl_ptr;
+	size_t uboot_size, spl_size;
+
+	out_fd = open_output_bin(arg->out_bin);
+	if (0 > out_fd)
+		goto err;
+
+	spl_fd = open_input_bin(arg->spl_bin, &spl_ptr, &spl_size);
+	if (0 > spl_fd)
+		goto err_spl;
+
+	uboot_fd = open_input_bin(arg->uboot_bin, &uboot_ptr, &uboot_size);
+	if (0 > uboot_fd)
+		goto err_uboot;
+
+	ret = write_nvb_dwnld_header(out_fd, spl_size, arg->entry_addr);
+	if (ret)
+		goto err_write;
+
+	ret = copy_bin(out_fd, spl_ptr, spl_size);
+	if (ret)
+		goto err_write;
+
+	ret = write_nvb_start_header(out_fd, arg->entry_addr);
+	if (ret)
+		goto err_write;
+
+	ret = copy_bin(out_fd, uboot_ptr, uboot_size);
+	if (ret)
+		goto err_write;
+
+	close_input_bin(uboot_fd, uboot_ptr, uboot_size);
+	close_input_bin(spl_fd, spl_ptr, spl_size);
+	close(out_fd);
+
+	return 0;
+
+err_write:
+	close_input_bin(uboot_fd, uboot_ptr, uboot_size);
+err_uboot:
+	close_input_bin(spl_fd, spl_ptr, spl_size);
+err_spl:
+	close(out_fd);
+err:
+	return -1;
+}
+
+int main(int argc, char *argv[])
+{
+	int ret;
+	struct args arg;
+
+	ret = parse_args(argc, argv, &arg);
+	if (ret)
+		goto done;
+
+	switch (arg.type) {
+	case IMAGE_SFSPL:
+		ret = create_sfspl(&arg);
+		break;
+	default:
+		fprintf(stderr, "Image type not implemented\n");
+		ret = -1;
+		break;
+	}
+
+done:
+	if (ret >= 0)
+		return EXIT_SUCCESS;
+
+	return EXIT_FAILURE;
+}
